diff options
Diffstat (limited to '')
51 files changed, 23373 insertions, 0 deletions
diff --git a/private/utils/ufat/dirs b/private/utils/ufat/dirs new file mode 100644 index 000000000..c4db5a979 --- /dev/null +++ b/private/utils/ufat/dirs @@ -0,0 +1,16 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + dirs. + +Abstract: + + This file specifies the subdirectories of the current directory that + contain component makefiles. + +!ENDIF + +DIRS= src diff --git a/private/utils/ufat/inc/cluster.hxx b/private/utils/ufat/inc/cluster.hxx new file mode 100644 index 000000000..3c9a82288 --- /dev/null +++ b/private/utils/ufat/inc/cluster.hxx @@ -0,0 +1,178 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + cluster.hxx + +Abstract: + + This class models a chain of clusters in a FAT file system. It gives + the ability to refer to a scattered chain of clusters as one contiguous + region of memory. This memory will be acquired from a MEM object at + initialization. + +Author: + + Norbert P. Kusters (norbertk) 27-Nov-90 + +--*/ + +#if !defined(CLUSTER_CHAIN_DEFN) + +#define CLUSTER_CHAIN_DEFN + + +#include "secrun.hxx" +#include "hmem.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( CLUSTER_CHAIN ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); +DECLARE_CLASS( MEM ); + +class CLUSTER_CHAIN : public OBJECT { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR( CLUSTER_CHAIN ); + + VIRTUAL + UFAT_EXPORT + ~CLUSTER_CHAIN( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT ClusterNumber, + IN USHORT LengthOfChain DEFAULT 0 + ); + + VIRTUAL + UFAT_EXPORT + BOOLEAN + Read( + ); + + VIRTUAL + UFAT_EXPORT + BOOLEAN + Write( + ); + + NONVIRTUAL + PVOID + GetBuf( + ); + + NONVIRTUAL + USHORT + QueryLength( + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + PSECRUN* _secruns; + USHORT _num_secruns; + USHORT _length_of_chain; + + // + // Stuff needed for compressed volumes. + // + + BOOLEAN _is_compressed; + PSECRUN _secrun; + PUCHAR _buf; + HMEM _hmem; + PFAT_SA _fat_sa; + PCFAT _fat; + PLOG_IO_DP_DRIVE + _drive; + USHORT _starting_cluster; +}; + + +INLINE +PVOID +CLUSTER_CHAIN::GetBuf( + ) +/*++ + +Routine Description: + + This routine returns a pointer to the beginning of the memory map for + the cluster chain. + +Arguments: + + None. + +Return Value: + + A pointer to the beginning of the memory map for the cluster chain. + +--*/ +{ + if (_is_compressed) { + return _buf; + } + return (_secruns && _secruns[0]) ? _secruns[0]->GetBuf() : NULL; +} + + +INLINE +USHORT +CLUSTER_CHAIN::QueryLength( + ) CONST +/*++ + +Routine Description: + + Computes the number of clusters in the cluster chain. + +Arguments: + + None. + +Return Value: + + The number of clusters in the cluster chain. + +--*/ +{ + return _length_of_chain; +} + + +#endif // CLUSTER_CHAIN_DEFN diff --git a/private/utils/ufat/inc/cvf.hxx b/private/utils/ufat/inc/cvf.hxx new file mode 100644 index 000000000..e105beff8 --- /dev/null +++ b/private/utils/ufat/inc/cvf.hxx @@ -0,0 +1,251 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + cvf.hxx + +Abstract: + + This module contains basic declarations and definitions for + the double-space file system format. Note that more extensive + description of the file system structures may be found in + ntos\fastfat\cvf.h + +Author: + + Bill McJohn [BillMc] 24-September-1993 + +Revision History: + + + +--*/ + +#if !defined( _UFATDB_DEFN_ ) +#define _UFATDB_DEFN_ + +#include "bpb.hxx" + +// Manifest constants for fixed values on a Double Space drive: +// +CONST DoubleSpaceBytesPerSector = 512; +CONST DoubleSpaceLog2BytesPerSector = 9; +CONST DoubleSpaceSectorsPerCluster = 16; +CONST DoubleSpaceLog2SectorsPerCluster = 4; +CONST DoubleSpaceReservedSectors = 16; +CONST DoubleSpaceFats = 1; +CONST DoubleSpaceSectorsInRootDir = 32; +CONST DoubleSpaceRootEntries = 512; +CONST DoubleSpaceMediaByte = 0xf8; +CONST DoubleSpaceSectorsPerTrack = 0x11; +CONST DoubleSpaceHeads = 6; +CONST DoubleSpaceHiddenSectors = 0; +CONST DoubleSpaceReservedSectors2 = 31; +CONST DSBytesPerBitmapPage = 2048; +CONST DSSectorsPerBitmapPage = 4; + +CONST ULONG EIGHT_MEG = 8 * 1024L * 1024L; + +CONST DbSignatureLength = 4; +CONST UCHAR FirstDbSignature[DbSignatureLength] = { (UCHAR)0xf8, 'D', 'R', 0 }; +CONST UCHAR SecondDbSignature[DbSignatureLength] = "MDR"; + +INLINE +ULONG +ComputeMaximumCapacity( + IN ULONG HostDriveSize + ) +/*++ + +Routine Description: + + This function computes the maximum capacity for a compressed + volume file on a host volume of a given size. + +Arguments: + + HostDriveSize -- Supplies the size in bytes of the host drive. + +Return Value: + + The appropriate Maximum Capacity. + +--*/ +{ + ULONG MaxCap; + + if( HostDriveSize < 20 * 1024L * 1024L ) { + + MaxCap = 16 * HostDriveSize; + + } else if ( HostDriveSize < 64 * 1024L * 1024L ) { + + MaxCap = 8 * HostDriveSize; + + } else { + + MaxCap = 4 * HostDriveSize; + } + + if( MaxCap < 4 * 1024L * 1024L ) { + + MaxCap = 4 * 1024L * 1024L; + + } else if( MaxCap > 512 * 1024L * 1024L ) { + + MaxCap = 512 * 1024L * 1024L; + } + + return MaxCap; +} + +typedef struct _PACKED_CVF_HEADER { + + // + // First a typical start of a boot sector + // + + UCHAR Jump[1]; // offset = 0x000 0 + UCHAR JmpOffset[2]; + UCHAR Oem[8]; // offset = 0x003 3 + PACKED_BIOS_PARAMETER_BLOCK PackedBpb; // offset = 0x00B 11 + + // + // Now the DblSpace extensions + // + + UCHAR CvfFatExtensionsLbnMinus1[2]; // offset = 0x024 36 + UCHAR LogOfBytesPerSector[1]; // offset = 0x026 38 + UCHAR DosBootSectorLbn[2]; // offset = 0x027 39 + UCHAR DosRootDirectoryOffset[2]; // offset = 0x029 41 + UCHAR CvfHeapOffset[2]; // offset = 0x02B 43 + UCHAR CvfFatFirstDataEntry[2]; // offset = 0x02D 45 + UCHAR CvfBitmap2KSize[1]; // offset = 0x02F 47 + UCHAR Reserved1[2]; // offset = 0x030 48 + UCHAR LogOfSectorsPerCluster[1]; // offset = 0x032 50 + UCHAR Reserved2[2]; // offset = 0x033 + UCHAR MinFile[4]; // offset = 0x035 + UCHAR Reserved3[4]; // offset = 0x039 + UCHAR Is12BitFat[1]; // offset = 0x03D 61 + UCHAR CvfMaximumCapacity[2]; // offset = 0x03E 62 + UCHAR StartBootCode; + +} PACKED_CVF_HEADER; // sizeof = 0x040 64 +typedef PACKED_CVF_HEADER *PPACKED_CVF_HEADER; + +// +// For the unpacked version we'll only define the necessary field and skip +// the jump and oem fields. +// + +typedef struct _CVF_HEADER { + + UCHAR Jump; + USHORT JmpOffset; + + UCHAR Oem[8]; + BIOS_PARAMETER_BLOCK Bpb; + + USHORT CvfFatExtensionsLbnMinus1; + UCHAR LogOfBytesPerSector; + USHORT DosBootSectorLbn; + USHORT DosRootDirectoryOffset; + USHORT CvfHeapOffset; + USHORT CvfFatFirstDataEntry; + UCHAR CvfBitmap2KSize; + UCHAR LogOfSectorsPerCluster; + UCHAR Is12BitFat; + ULONG MinFile; + USHORT CvfMaximumCapacity; + +} CVF_HEADER; +typedef CVF_HEADER *PCVF_HEADER; + +// +// Here is NT's typical routine/macro to unpack the cvf header because DOS +// doesn't bother to naturally align anything. +// +// VOID +// CvfUnpackCvfHeader ( +// IN OUT PCVF_HEADER UnpackedHeader, +// IN PPACKED_CVF_HEADER PackedHeader +// ); +// + +#define CvfUnpackCvfHeader(UH,PH) { \ + \ + memcpy( &(UH)->Jump, &(PH)->Jump, 1 ); \ + memcpy( &(UH)->JmpOffset, &(PH)->JmpOffset, 2 ); \ + memcpy( &(UH)->Oem, &(PH)->Oem, 8 ); \ + UnpackBios( &(UH)->Bpb, &(PH)->PackedBpb ); \ + CopyUchar2( &(UH)->CvfFatExtensionsLbnMinus1, &(PH)->CvfFatExtensionsLbnMinus1[0] ); \ + CopyUchar1( &(UH)->LogOfBytesPerSector, &(PH)->LogOfBytesPerSector[0] ); \ + CopyUchar2( &(UH)->DosBootSectorLbn, &(PH)->DosBootSectorLbn[0] ); \ + CopyUchar2( &(UH)->DosRootDirectoryOffset, &(PH)->DosRootDirectoryOffset[0] ); \ + CopyUchar2( &(UH)->CvfHeapOffset, &(PH)->CvfHeapOffset[0] ); \ + CopyUchar2( &(UH)->CvfFatFirstDataEntry, &(PH)->CvfFatFirstDataEntry[0] ); \ + CopyUchar1( &(UH)->CvfBitmap2KSize, &(PH)->CvfBitmap2KSize[0] ); \ + CopyUchar1( &(UH)->LogOfSectorsPerCluster, &(PH)->LogOfSectorsPerCluster[0] ); \ + CopyUchar1( &(UH)->Is12BitFat, &(PH)->Is12BitFat[0] ); \ + CopyUchar4( &(UH)->MinFile, &(PH)->MinFile[0] ); \ + CopyUchar2( &(UH)->CvfMaximumCapacity, &(PH)->CvfMaximumCapacity[0] ); \ +} + + +#define CvfPackCvfHeader(PH,UH) { \ + \ + memcpy( &(PH)->Jump, &(UH)->Jump, 1 ); \ + memcpy( &(PH)->JmpOffset, &(UH)->JmpOffset, 2 ); \ + memcpy( &(PH)->Oem, &(UH)->Oem, 8 ); \ + PackBios( &(UH)->Bpb, &(PH)->PackedBpb, ); \ + CopyUchar2( (PH)->CvfFatExtensionsLbnMinus1, &(UH)->CvfFatExtensionsLbnMinus1 ); \ + CopyUchar1( (PH)->LogOfBytesPerSector, &(UH)->LogOfBytesPerSector ); \ + CopyUchar2( (PH)->DosBootSectorLbn, &(UH)->DosBootSectorLbn ); \ + CopyUchar2( (PH)->DosRootDirectoryOffset, &(UH)->DosRootDirectoryOffset ); \ + CopyUchar2( (PH)->CvfHeapOffset, &(UH)->CvfHeapOffset ); \ + CopyUchar2( (PH)->CvfFatFirstDataEntry, &(UH)->CvfFatFirstDataEntry ); \ + CopyUchar1( (PH)->CvfBitmap2KSize, &(UH)->CvfBitmap2KSize ); \ + CopyUchar1( (PH)->LogOfSectorsPerCluster, &(UH)->LogOfSectorsPerCluster ); \ + CopyUchar1( (PH)->Is12BitFat, &(UH)->Is12BitFat ); \ + CopyUchar4( (PH)->MinFile, &(UH)->MinFile ); \ + CopyUchar2( (PH)->CvfMaximumCapacity, &(UH)->CvfMaximumCapacity ); \ + memset( (PH)->Reserved1, 0, 2 ); \ + memset( (PH)->Reserved2, 0, 2 ); \ + memset( (PH)->Reserved3, 0, 4 ); \ +} + + +// +// The CVF FAT EXTENSIONS is a table is ULONG entries. Each entry corresponds +// to a FAT cluster. The entries describe where in the CVF_HEAP to locate +// the data for the cluster. It indicates if the data is compressed and the +// length of the compressed and uncompressed form. +// + +typedef struct _CVF_FAT_EXTENSIONS { + + ULONG CvfHeapLbnMinus1 : 21; + ULONG Reserved : 1; + ULONG CompressedSectorLengthMinus1 : 4; + ULONG UncompressedSectorLengthMinus1 : 4; + ULONG IsDataUncompressed : 1; + ULONG IsEntryInUse : 1; + +} CVF_FAT_EXTENSIONS; +typedef CVF_FAT_EXTENSIONS *PCVF_FAT_EXTENSIONS; + + +// +// Some sizes are fixed so we'll declare them as manifest constants +// +#define CVF_MINIMUM_DISK_SIZE (512 * 1024L) +#define CVF_FATFAILSAFE (1024L) +#define CVF_MIN_HEAP_SECTORS (60) +#define CVF_RESERVED_AREA_1_SECTOR_SIZE (1) +#define CVF_RESERVED_AREA_2_SECTOR_SIZE (31) +#define CVF_RESERVED_AREA_4_SECTOR_SIZE (2) + + +#endif // _UFATDB_DEFN_ diff --git a/private/utils/ufat/inc/cvfexts.hxx b/private/utils/ufat/inc/cvfexts.hxx new file mode 100644 index 000000000..7a7bcda9f --- /dev/null +++ b/private/utils/ufat/inc/cvfexts.hxx @@ -0,0 +1,294 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + cvfexts.hxx + +Abstract: + + A class to manage the CVF_FAT_EXTENSIONS (otherwise known as + the MDFAT) part of the doublespace volume. + +Author: + + Matthew Bradburn (mattbr) 27-Sep-93 + +--*/ + +#ifndef CVF_EXTS_DEFN +#define CVF_EXTS_DEFN + +// +// secStart : 21 starting sector number, minus 1 +// Reserved : 1 unused +// csecCoded : 4 number of compressed sectors required, minus 1 +// csecPlain : 4 number of uncompressed sectors required, minus 1 +// fUncoded : 1 0: data stored compressed 1: data uncompressed +// fUsed : 1 0: mdfat entry not in use 1: mdfat entry in use +// + +#define CFE_START_SHIFT 0 +#define CFE_START_MASK 0x001fffff + +#define CFE_RESVD_SHIFT 21 +#define CFE_RESVD_MASK 0x00200000 + +#define CFE_CODED_SHIFT 22 +#define CFE_CODED_MASK 0x03c00000 + +#define CFE_PLAIN_SHIFT 26 +#define CFE_PLAIN_MASK 0x3c000000 + +#define CFE_UNCODED_SHIFT 30 +#define CFE_UNCODED_MASK 0x40000000 + +#define CFE_USED_SHIFT 31 +#define CFE_USED_MASK 0x80000000 + + +DECLARE_CLASS( CVF_FAT_EXTENS ); + +class CVF_FAT_EXTENS : public SECRUN { + public: + DECLARE_CONSTRUCTOR(CVF_FAT_EXTENS); + + NONVIRTUAL + ~CVF_FAT_EXTENS(); + + NONVIRTUAL + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartSetor, + IN ULONG NumberOfEntries, + IN ULONG FirstEntry + ); + + NONVIRTUAL + BOOLEAN + Create( + ); + + NONVIRTUAL + BOOLEAN + IsClusterInUse( + IN ULONG Cluster + ) CONST; + + NONVIRTUAL + VOID + SetClusterInUse( + IN ULONG Cluster, + IN BOOLEAN fInUse + ); + + NONVIRTUAL + ULONG + QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors DEFAULT NULL + ); + + NONVIRTUAL + VOID + SetSectorForCluster( + IN ULONG Cluster, + IN ULONG Sector, + IN UCHAR SectorCount + ); + + NONVIRTUAL + BOOLEAN + IsClusterCompressed( + IN ULONG Cluster + ) CONST; + + NONVIRTUAL + VOID + SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN fCompressed + ); + + NONVIRTUAL + UCHAR + QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ) CONST; + + NONVIRTUAL + VOID + SetSectorsRequiredForPlainData( + IN ULONG Cluster, + IN UCHAR SectorsRequired + ); + + private: + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + PULONG _mdfat; + ULONG _num_entries; + ULONG _first_entry; +}; + +INLINE BOOLEAN +CVF_FAT_EXTENS::IsClusterInUse( + IN ULONG Cluster + ) CONST +{ + ULONG CfeEntry = _mdfat[Cluster]; + + return BOOLEAN((CfeEntry & CFE_USED_MASK) >> CFE_USED_SHIFT); +} + +INLINE VOID +CVF_FAT_EXTENS::SetClusterInUse( + IN ULONG Cluster, + IN BOOLEAN fInUse + ) +{ + ULONG CfeEntry = _mdfat[Cluster]; + + CfeEntry &= ~CFE_USED_MASK; + CfeEntry |= fInUse << CFE_USED_SHIFT; + + _mdfat[Cluster] = CfeEntry; +} + +INLINE ULONG +CVF_FAT_EXTENS::QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors + ) +/*++ + +Routine Description: + + This routine takes a cluster number and uses the mdfat to return + the sector number in which the data starts. Also the number of + sectors used to store the cluster data is returned. Works for + compressed and uncompressed sectors as well, and for clusters + whether they're "In Use" or not. The number returned is the + value from the CVF_FAT_EXTENSIONS plus 1. + +Arguments: + + Cluster - the cluster to get info for. + +Return Value: + + ULONG - the CVF starting sector number. + +--*/ +{ + ULONG CfeEntry = _mdfat[Cluster]; + + if (NULL != NumSectors) { + *NumSectors = (UCHAR)((CfeEntry & CFE_CODED_MASK) >> CFE_CODED_SHIFT) + 1; + } + + return ((CfeEntry & CFE_START_MASK) >> CFE_START_SHIFT) + 1; +} + +INLINE VOID +CVF_FAT_EXTENS::SetSectorForCluster( + IN ULONG Cluster, + IN ULONG Sector, + IN UCHAR SectorCount + ) +/*++ + +Routine Description: + + This routine sets the cluster->sector mapping, as well as + the number of sectors required to store the compressed cluster. + +Arguments: + + Cluster - cluster number + Sector - starting CVF sector number + SectorCount - number of sectors required + +Return Value: + + None. + +--*/ +{ + ULONG CfeEntry = _mdfat[Cluster]; + + DbgAssert(Sector > 0); + DbgAssert(SectorCount > 0); + + CfeEntry &= ~CFE_START_MASK; + CfeEntry |= (Sector - 1) << CFE_START_SHIFT; + + CfeEntry &= ~CFE_CODED_MASK; + CfeEntry |= ((ULONG)(SectorCount - 1) << CFE_CODED_SHIFT); + + _mdfat[Cluster] = CfeEntry; +} + +INLINE UCHAR +CVF_FAT_EXTENS::QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ) CONST +{ + ULONG CfeEntry = _mdfat[Cluster]; + + return (UCHAR)((CfeEntry & CFE_PLAIN_MASK) >> CFE_PLAIN_SHIFT) + 1; +} + +INLINE VOID +CVF_FAT_EXTENS::SetSectorsRequiredForPlainData( + IN ULONG Cluster, + IN UCHAR SectorsRequired + ) +{ + ULONG CfeEntry = _mdfat[Cluster]; + + DbgAssert(SectorsRequired > 0); + + CfeEntry &= ~CFE_PLAIN_MASK; + CfeEntry |= (ULONG)(SectorsRequired - 1) << CFE_PLAIN_SHIFT; + + _mdfat[Cluster] = CfeEntry; +} + +INLINE BOOLEAN +CVF_FAT_EXTENS::IsClusterCompressed( + IN ULONG Cluster + ) CONST +{ + ULONG CfeEntry = _mdfat[Cluster]; + + return ! ((CfeEntry & CFE_UNCODED_MASK) >> CFE_UNCODED_SHIFT); +} + +INLINE VOID +CVF_FAT_EXTENS::SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN fCompressed + ) +{ + ULONG CfeEntry = _mdfat[Cluster]; + + CfeEntry &= ~CFE_UNCODED_MASK; + CfeEntry |= (ULONG)!fCompressed << CFE_UNCODED_SHIFT; + + _mdfat[Cluster] = CfeEntry; +} + +#endif // CVF_EXTS_DEFN diff --git a/private/utils/ufat/inc/dblentry.hxx b/private/utils/ufat/inc/dblentry.hxx new file mode 100644 index 000000000..05f514bea --- /dev/null +++ b/private/utils/ufat/inc/dblentry.hxx @@ -0,0 +1,68 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + ufatdb.hxx + +Abstract: + + This module contains prototypes for the Double Space utility + entry points. + +Author: + + Bill McJohn [BillMc] 24-September-1993 + +Revision History: + +--*/ + +DECLARE_CLASS( WSTRING ); +DECLARE_CLASS( MESSAGE ); + +extern "C" +BOOLEAN +FAR APIENTRY +FatDbFormat( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Quick, + IN MEDIA_TYPE MediaType, + IN PCWSTRING LabelString, + IN ULONG ClusterSize + ); + +extern "C" +BOOLEAN +FAR APIENTRY +FatDbChkdsk( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Fix, + IN BOOLEAN Verbose, + IN BOOLEAN OnlyIfDirty, + IN BOOLEAN Recover, + IN PCWSTRING PathToCheck + ); + +extern "C" +BOOLEAN +FAR APIENTRY +FatDbCreate( + IN PCWSTRING HostDriveName, + IN PCWSTRING HostFileName, + IN ULONG Size, + IN OUT PMESSAGE Message, + IN PCWSTRING Label, + OUT PWSTRING CreatedName + ); + +extern "C" +BOOLEAN +FAR APIENTRY +FatDbDelete( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message + ); diff --git a/private/utils/ufat/inc/eaheader.hxx b/private/utils/ufat/inc/eaheader.hxx new file mode 100644 index 000000000..1323f78dd --- /dev/null +++ b/private/utils/ufat/inc/eaheader.hxx @@ -0,0 +1,229 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + eaheader.hxx + +Abstract: + + This class models the header and tables of the EA file. + +Author: + + Norbert P. Kusters (norbertk) 28-Nov-90 + +Notes: + + Luckily all the structures are well aligned. + +--*/ + +#if !defined(EA_HEADER_DEFN) + +#define EA_HEADER_DEFN + +#include "cluster.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( EA_HEADER ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); +DECLARE_CLASS( MEM ); + + +struct _EA_FILE_HEADER { + USHORT Signature; + USHORT FormatType; + USHORT LogType; + USHORT Cluster1; + USHORT NewCValue1; + USHORT Cluster2; + USHORT NewCValue2; + USHORT Cluster3; + USHORT NewCValue3; + USHORT Handle; + USHORT NewHOffset; + UCHAR Reserved[10]; +}; + +DEFINE_TYPE( struct _EA_FILE_HEADER, EA_FILE_HEADER ); + +CONST BaseTableSize = 240; + +struct _EA_MAP_TBL { + USHORT BaseTab[BaseTableSize]; + USHORT OffTab[1]; +}; + +DEFINE_TYPE( struct _EA_MAP_TBL, EA_MAP_TBL ); + +struct _EA_HEADER_AND_TABLE { + EA_FILE_HEADER Header; + EA_MAP_TBL Table; +}; + +DEFINE_TYPE( struct _EA_HEADER_AND_TABLE, EA_HEADER_AND_TABLE ); + +CONST USHORT HeaderSignature = 0x4445; +CONST USHORT CurrentFormatType = 0; +CONST USHORT CurrentLogType = 0; +CONST USHORT Bit15 = 0x8000; +CONST USHORT InvalidHandle = 0xFFFF; + + +class EA_HEADER : public CLUSTER_CHAIN { + + public: + UFAT_EXPORT + DECLARE_CONSTRUCTOR( EA_HEADER ); + + VIRTUAL + UFAT_EXPORT + ~EA_HEADER( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT StartingCluster, + IN USHORT LengthOfChain DEFAULT 0 + ); + + NONVIRTUAL + PEA_FILE_HEADER + GetEaFileHeader( + ); + + NONVIRTUAL + PEA_MAP_TBL + GetMapTable( + ); + + NONVIRTUAL + LONG + QueryOffTabSize( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + QueryEaSetClusterNumber( + IN USHORT Handle + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + PEA_HEADER_AND_TABLE _ht; + LONG _off_tab_size; + +}; + + +INLINE +PEA_FILE_HEADER +EA_HEADER::GetEaFileHeader( + ) +/*++ + +Routine Description: + + This routine returns a pointer to the EA file header. Dereferencing + this pointer will allow the client to examine and modify the + EA file header. These changes will take effect on disk when the + client issues a 'Write' command. + +Arguments: + + None. + +Return Value: + + A pointer to the EA file header. + +--*/ +{ + return _ht ? &_ht->Header : NULL; +} + + +INLINE +PEA_MAP_TBL +EA_HEADER::GetMapTable( + ) +/*++ + +Routine Description: + + This routine returns a pointer to the EA mapping table. Dereferencing + this pointer will allow the client to examine and modify the + EA mapping table. These changes will take effect on disk when the + client issues a 'Write' command. + +Arguments: + + None. + +Return Value: + + A pointer to the EA mapping table. + +--*/ +{ + return _ht ? &_ht->Table : NULL; +} + + +INLINE +LONG +EA_HEADER::QueryOffTabSize( + ) CONST +/*++ + +Routine Description: + + Computes the number of entries in the offset table. + +Arguments: + + None. + +Return Value: + + Returns the number of entries in the offset table. + +--*/ +{ + return _off_tab_size; +} + + +#endif // EA_HEADER_DEFN diff --git a/private/utils/ufat/inc/easet.hxx b/private/utils/ufat/inc/easet.hxx new file mode 100644 index 000000000..93208f4ca --- /dev/null +++ b/private/utils/ufat/inc/easet.hxx @@ -0,0 +1,243 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + easet.hxx + +Abstract: + + This class models an EA set. + +Author: + + Norbert P. Kusters (norbertk) 28-Nov-90 + +Notes: + + There are minor alignment problems here. + +--*/ + +#if !defined(EA_SET_DEFN) + +#define EA_SET_DEFN + +#include "cluster.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( EA_SET ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); +DECLARE_CLASS( MEM ); + + +struct _EA_HDR { + USHORT Signature; + USHORT OwnHandle; + ULONG NeedCount; + UCHAR OwnerFileName[14]; + ULONG Reserved; + LONG TotalSize; +}; + +DEFINE_TYPE( struct _EA_HDR, EA_HDR ); + +struct _PACKED_EA_HDR { + USHORT Signature; + USHORT OwnHandle; + ULONG NeedCount; + UCHAR OwnerFileName[14]; + UCHAR Reserved[4]; + UCHAR TotalSize[4]; +}; + +DEFINE_TYPE( struct _PACKED_EA_HDR, PACKED_EA_HDR ); + +const SizeOfEaHdr = 30; // sizeof returns 32. + +struct _EA { + UCHAR Flag; + UCHAR NameSize; + UCHAR ValueSize[2]; // Was USHORT. + CHAR Name[1]; +}; + +DEFINE_TYPE( struct _EA, EA ); + +CONST USHORT EaSetSignature = 0x4145; +CONST UCHAR NeedFlag = 0x80; + + +class EA_SET : public CLUSTER_CHAIN { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR( EA_SET ); + + VIRTUAL + UFAT_EXPORT + ~EA_SET( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT ClusterNumber, + IN USHORT LengthOfChain DEFAULT 0 + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Read( + ); + + NONVIRTUAL + BOOLEAN + Write( + ); + + NONVIRTUAL + PEA_HDR + GetEaSetHeader( + ); + + NONVIRTUAL + UFAT_EXPORT + PEA + GetEa( + IN ULONG Index, + OUT PLONG EaSize DEFAULT NULL, + OUT PBOOLEAN PossiblyMore DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + VerifySignature( + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + NONVIRTUAL + BOOLEAN + PackEaHeader( + ); + + NONVIRTUAL + BOOLEAN + UnPackEaHeader( + ); + + EA_HDR _eahdr; + LONG _size; + BOOLEAN _size_imposed; + PEA _current_ea; + ULONG _current_index; + +}; + +INLINE +BOOLEAN +EA_SET::Write( + ) +/*++ + +Routine Description: + + This routine packs the ea header and then writes the cluster chain to disk. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return PackEaHeader() && CLUSTER_CHAIN::Write(); +} + + +INLINE +PEA_HDR +EA_SET::GetEaSetHeader( + ) +/*++ + +Routine Description: + + This routine returns a pointer to the unpacked ea set header. + +Arguments: + + None. + +Return Value: + + A pointer to the unpacked ea set header. + +--*/ +{ + return &_eahdr; +} + + +INLINE +BOOLEAN +EA_SET::VerifySignature( + ) CONST +/*++ + +Routine Description: + + This routine verifies the signature on the EA set. + +Arguments: + + None. + +Return Value: + + FALSE - The signature is invalid. + TRUE - The signature is valid. + +--*/ +{ + return _eahdr.Signature == EaSetSignature; +} + + +#endif // EA_SET_DEFN diff --git a/private/utils/ufat/inc/fat.hxx b/private/utils/ufat/inc/fat.hxx new file mode 100644 index 000000000..565adb837 --- /dev/null +++ b/private/utils/ufat/inc/fat.hxx @@ -0,0 +1,904 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fat.hxx + +Abstract: + + This class models a file allocation table. The composition is of + virtual functions because there are two different kinds of file + allocation tables. A user of this class will be able to manipulate + the FAT regardless of the implementation. + +Author: + + Norbert P. Kusters (norbertk) 6-Dec-90 + +--*/ + +#if !defined(FAT_DEFN) + +#define FAT_DEFN + +#include "secrun.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( FAT ); +DECLARE_CLASS( BITVECTOR ); + +CONST FirstDiskCluster = 2; +CONST MaxNumClusForSmallFat = 4085; + +class FAT : public SECRUN { + + public: + + DECLARE_CONSTRUCTOR(FAT); + + VIRTUAL + ~FAT( + ); + + NONVIRTUAL + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartSector, + IN USHORT NumberOfEntries, + IN USHORT NumSectors DEFAULT 0 + ); + + NONVIRTUAL + USHORT + QueryEntry( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + SetEntry( + IN USHORT ClusterNumber, + IN USHORT Value + ); + + NONVIRTUAL + BOOLEAN + IsInRange( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + BOOLEAN + IsClusterFree( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + SetClusterFree( + IN USHORT ClusterNumber + ); + + NONVIRTUAL + BOOLEAN + IsEndOfChain( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + SetEndOfChain( + IN USHORT ClusterNumber + ); + + NONVIRTUAL + BOOLEAN + IsClusterBad( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + SetClusterBad( + IN USHORT ClusterNumber + ); + + NONVIRTUAL + BOOLEAN + IsClusterReserved( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + SetClusterReserved( + IN USHORT ClusterNumber + ); + + NONVIRTUAL + VOID + SetEarlyEntries( + IN UCHAR MediaByte + ); + + NONVIRTUAL + UCHAR + QueryMediaByte( + ) CONST; + + NONVIRTUAL + USHORT + QueryFreeClusters( + ) CONST; + + NONVIRTUAL + USHORT + QueryBadClusters( + ) CONST; + + NONVIRTUAL + USHORT + QueryReservedClusters( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + QueryAllocatedClusters( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + QueryNthCluster( + IN USHORT StartingCluster, + IN USHORT Index + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + QueryLengthOfChain( + IN USHORT StartingCluster, + OUT PUSHORT LastCluster DEFAULT NULL + ) CONST; + + NONVIRTUAL + USHORT + QueryLengthOfChain( + IN USHORT StartingCluster, + IN USHORT EndingCluster + ) CONST; + + NONVIRTUAL + USHORT + QueryPrevious( + IN USHORT Cluster + ) CONST; + + NONVIRTUAL + VOID + Scrub( + OUT PBOOLEAN ChangesMade DEFAULT NULL + ); + + NONVIRTUAL + VOID + ScrubChain( + IN USHORT StartingCluster, + OUT PBOOLEAN ChangesMade + ); + + NONVIRTUAL + VOID + ScrubChain( + IN USHORT StartingCluster, + OUT PBITVECTOR UsedClusters, + OUT PBOOLEAN ChangesMade, + OUT PBOOLEAN CrossLinkDetected, + OUT PUSHORT CrossLinkPreviousCluster + ); + + NONVIRTUAL + BOOLEAN + IsValidChain( + IN USHORT StartingCluster + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + AllocChain( + IN USHORT Length, + OUT PUSHORT LastCluster DEFAULT NULL + ); + + NONVIRTUAL + USHORT + ReAllocChain( + IN USHORT StartOfChain, + IN USHORT NewLength, + OUT PUSHORT LastCluster DEFAULT NULL + ); + + NONVIRTUAL + UFAT_EXPORT + VOID + FreeChain( + IN USHORT StartOfChain + ); + + NONVIRTUAL + USHORT + RemoveChain( + IN USHORT PreceedingCluster, + IN USHORT LastCluster + ); + + NONVIRTUAL + VOID + InsertChain( + IN USHORT StartOfChain, + IN USHORT EndOfChain, + IN USHORT PreceedingCluster + ); + + NONVIRTUAL + USHORT + InsertChain( + IN USHORT StartOfChain, + IN USHORT Cluster + ); + + private: + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + NONVIRTUAL + USHORT + Index( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + USHORT + Index12( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + USHORT + Index16( + IN USHORT ClusterNumber + ) CONST; + + NONVIRTUAL + VOID + Set( + IN USHORT ClusterNumber, + IN USHORT Value + ); + + NONVIRTUAL + UFAT_EXPORT + VOID + Set12( + IN USHORT ClusterNumber, + IN USHORT Value + ); + + NONVIRTUAL + VOID + Set16( + IN USHORT ClusterNumber, + IN USHORT Value + ); + + PVOID _fat; + USHORT _num_entries; + BOOLEAN _is_big; + USHORT _low_end_of_chain; // 0xFFF8 or 0x0FF8 + USHORT _end_of_chain; // 0xFFFF or 0x0FFF + USHORT _bad_cluster; // 0xFFF7 or 0x0FF7 + USHORT _low_reserved; // 0xFFF0 or 0x0FF0 + USHORT _high_reserved; // 0xFFF6 or 0x0FF6 + +}; + + +INLINE +BOOLEAN +FAT::IsInRange( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not ClusterNumber is a cluster on + the disk. + +Arguments: + + ClusterNumber - Supplies the cluster to be checked. + +Return Value: + + FALSE - The cluster is not on the disk. + TRUE - The cluster is on the disk. + +--*/ +{ + return FirstDiskCluster <= ClusterNumber && ClusterNumber < _num_entries; +} + + +INLINE +USHORT +FAT::Index16( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine indexes the FAT as 16 bit little endian entries. + +Arguments: + + ClusterNumber - Supplies the FAT entry desired. + +Return Value: + + The value of the FAT entry at ClusterNumber. + +--*/ +{ + DebugAssert(IsInRange(ClusterNumber)); + + return ((PUSHORT) _fat)[ClusterNumber]; +} + + +INLINE +USHORT +FAT::Index( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine indexes the FAT as 16 bit or 12 bit little endian entries. + +Arguments: + + ClusterNumber - Supplies the FAT entry desired. + +Return Value: + + The value of the FAT entry at ClusterNumber. + +--*/ +{ + return _is_big ? Index16(ClusterNumber) : Index12(ClusterNumber); +} + + +INLINE +VOID +FAT::Set16( + IN USHORT ClusterNumber, + IN USHORT Value + ) +/*++ + +Routine Description: + + This routine sets the ClusterNumber'th 16 bit FAT entry to Value. + +Arguments: + + ClusterNumber - Supplies the FAT entry to set. + Value - Supplies the value to set the FAT entry to. + +Return Value: + + None. + +--*/ +{ + DebugAssert(IsInRange(ClusterNumber)); + ((PUSHORT) _fat)[ClusterNumber] = Value; +} + + +INLINE +VOID +FAT::Set( + IN USHORT ClusterNumber, + IN USHORT Value + ) +/*++ + +Routine Description: + + This routine sets the ClusterNumber'th 12 bit or 16 bit FAT entry to Value. + +Arguments: + + ClusterNumber - Supplies the FAT entry to set. + Value - Supplies the value to set the FAT entry to. + +Return Value: + + None. + +--*/ +{ + _is_big ? Set16(ClusterNumber, Value) : Set12(ClusterNumber, Value); +} + + +INLINE +USHORT +FAT::QueryEntry( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine returns the FAT value for ClusterNumber. + +Arguments: + + ClusterNumber - Supplies an index into the FAT. + +Return Value: + + The FAT table entry at offset ClusterNumber. + +--*/ +{ + return Index(ClusterNumber); +} + + +INLINE +VOID +FAT::SetEntry( + IN USHORT ClusterNumber, + IN USHORT Value + ) +/*++ + +Routine Description: + + This routine sets the FAT entry at ClusterNumber to Value. + +Arguments: + + ClusterNumber - Supplies the position in the FAT to update. + Value - Supplies the new value for that position. + +Return Value: + + None. + +--*/ +{ + Set(ClusterNumber, Value); +} + + +INLINE +BOOLEAN +FAT::IsClusterFree( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine computes whether of not ClusterNumber is a free cluster. + +Arguments: + + ClusterNumber - Supplies the cluster to be checked. + +Return Value: + + FALSE - The cluster is not free. + TRUE - The cluster is free. + +--*/ +{ + return Index(ClusterNumber) == 0; +} + + +INLINE +VOID +FAT::SetClusterFree( + IN USHORT ClusterNumber + ) +/*++ + +Routine Description: + + This routine marks the cluster ClusterNumber as free on the FAT. + +Arguments: + + ClusterNumber - Supplies the number of the cluster to mark free. + +Return Value: + + None. + +--*/ +{ + Set(ClusterNumber, 0); +} + + +INLINE +BOOLEAN +FAT::IsEndOfChain( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the cluster ClusterNumber is the + end of its cluster chain. + +Arguments: + + ClusterNumber - Supplies the cluster to be checked. + +Return Value: + + FALSE - The cluster is not the end of a chain. + TRUE - The cluster is the end of a chain. + +--*/ +{ + return Index(ClusterNumber) >= _low_end_of_chain; +} + + +INLINE +VOID +FAT::SetEndOfChain( + IN USHORT ClusterNumber + ) +/*++ + +Routine Description: + + This routine sets the cluster ClusterNumber to the end of its cluster + chain. + +Arguments: + + ClusterNumber - Supplies the cluster to be set to end of chain. + +Return Value: + + None. + +--*/ +{ + Set(ClusterNumber, _end_of_chain); +} + + +INLINE +BOOLEAN +FAT::IsClusterBad( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not cluster ClusterNumber is bad. + +Arguments: + + ClusterNumber - Supplies the number of the cluster to be checked. + +Return Value: + + FALSE - The cluster is good. + TRUE - The cluster is bad. + +--*/ +{ + return Index(ClusterNumber) == _bad_cluster; +} + + +INLINE +VOID +FAT::SetClusterBad( + IN USHORT ClusterNumber + ) +/*++ + +Routine Description: + + This routine sets the cluster ClusterNumber to bad on the FAT. + +Arguments: + + ClusterNumber - Supplies the cluster number to mark bad. + +Return Value: + + None. + +--*/ +{ + Set(ClusterNumber, _bad_cluster); +} + + +INLINE +BOOLEAN +FAT::IsClusterReserved( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the cluster ClusterNumber is + a reserved cluster. + +Arguments: + + ClusterNumber - Supplies the cluster to check. + +Return Value: + + FALSE - The cluster is not reserved. + TRUE - The cluster is reserved. + +--*/ +{ + return Index(ClusterNumber) >= _low_reserved && + Index(ClusterNumber) <= _high_reserved; +} + + +INLINE +VOID +FAT::SetClusterReserved( + IN USHORT ClusterNumber + ) +/*++ + +Routine Description: + + This routine marks the cluster ClusterNumber as reserved in the FAT. + +Arguments: + + ClusterNumber - Supplies the cluster to mark reserved. + +Return Value: + + None. + +--*/ +{ + Set(ClusterNumber, _low_reserved); +} + + +INLINE +UCHAR +FAT::QueryMediaByte( + ) CONST +/*++ + +Routine Description: + + The media byte for the partition is stored in the first character of the + FAT. This routine will return its value provided that the two following + bytes are 0xFF. + +Arguments: + + None. + +Return Value: + + The media byte for the partition. + +--*/ +{ + PUCHAR p; + + p = (PUCHAR) _fat; + + DebugAssert(p); + + return (p[2] == 0xFF && p[1] == 0xFF && + (_is_big ? p[3] == 0xFF : TRUE)) ? p[0] : 0; +} + + +INLINE +VOID +FAT::SetEarlyEntries( + IN UCHAR MediaByte + ) +/*++ + +Routine Description: + + This routine sets the first two FAT entries as required by the + FAT file system. The first byte gets set to the media descriptor. + The remaining bytes gets set to FF. + +Arguments: + + MediaByte - Supplies the media byte for the volume. + +Return Value: + + None. + +--*/ +{ + PUCHAR p; + + p = (PUCHAR) _fat; + + DebugAssert(p); + + p[0] = MediaByte; + p[1] = p[2] = 0xFF; + + if (_is_big) { + p[3] = 0xFF; + } +} + + +INLINE +USHORT +FAT::RemoveChain( + IN USHORT PreceedingCluster, + IN USHORT LastCluster + ) +/*++ + +Routine Description: + + This routine removes a subchain of length 'Length' from a containing + chain. This routine cannot remove subchains beginning at the head + of the containing chain. To do this use the routine named + 'SplitChain'. + + This routine returns the number of the first cluster of the + removed subchain. The FAT is edited so that the removed subchain + is promoted to a full chain. + +Arguments: + + PreceedingCluster - Supplies the cluster which preceeds the one to be + removed in the chain. + LastCluster - Supplies the last cluster of the chain to remove. + +Return Value: + + The cluster number for the head of the chain removed. + +--*/ +{ + USHORT r; + + r = QueryEntry(PreceedingCluster); + SetEntry(PreceedingCluster, QueryEntry(LastCluster)); + SetEndOfChain(LastCluster); + return r; +} + + +INLINE +VOID +FAT::InsertChain( + IN USHORT StartOfChain, + IN USHORT EndOfChain, + IN USHORT PreceedingCluster + ) +/*++ + +Routine Description: + + This routine inserts one chain into another chain. This routine + cannot insert a chain at the head of another chain. To do this + use the routine named 'JoinChains'. + +Arguments: + + StartOfChain - Supplies the first cluster of the chain to insert. + EndOfChain - Supplies the last cluster of the chain to insert. + PreceedingCluster - Supplies the cluster immediately preceeding the + position where the chain is to be inserted. + +Return Value: + + None. + +--*/ +{ + SetEntry(EndOfChain, QueryEntry(PreceedingCluster)); + SetEntry(PreceedingCluster, StartOfChain); +} + + +INLINE +USHORT +FAT::InsertChain( + IN USHORT StartOfChain, + IN USHORT Cluster + ) +/*++ + +Routine Description: + + This routine inserts one cluster at the head of a chain. + +Arguments: + + StartOfChain - Supplies the first cluster of the chain to insert. + Cluster - Supplies the cluster to be inserted + +Return Value: + + USHORT - The new head of the chain (i.e. Cluster ) + +--*/ +{ + if ( StartOfChain ) { + SetEntry( Cluster, StartOfChain ); + } else { + SetEndOfChain( Cluster ); + } + + return Cluster; +} + + +#endif // FAT_DEFN diff --git a/private/utils/ufat/inc/fatdbsa.hxx b/private/utils/ufat/inc/fatdbsa.hxx new file mode 100644 index 000000000..1465ec739 --- /dev/null +++ b/private/utils/ufat/inc/fatdbsa.hxx @@ -0,0 +1,789 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatsa.hxx + +Abstract: + +Author: + + Matthew Bradburn (mattbr) 27-Sep-93 + +--*/ + +#if !defined(FATDB_SA_DEFN) +#define FATDB_SA_DEFN + +// +// Forward references +// + +#include "fatsa.hxx" +#include "cvfexts.hxx" +#include "cvf.hxx" +#include "bitvect.hxx" + +// the text for the oem data field +#define OEMDBTEXT "MSDSP6.0" + +DECLARE_CLASS( ARRAY ); +DECLARE_CLASS( BITVECTOR ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( FATDIR ); +DECLARE_CLASS( FAT_DIRENT ); +DECLARE_CLASS( CVF_FAT_EXTENS ); +DECLARE_CLASS( MESSAGE ); +DECLARE_CLASS( ROOTDIR ); +DECLARE_CLASS( SORTED_LIST ); +DECLARE_CLASS( TIMEINFO ); +DECLARE_CLASS( WSTRING ); +DECLARE_CLASS( FATDB_SA ); + +class FATDB_SA : public FAT_SA { + + public: + + DECLARE_CONSTRUCTOR(FATDB_SA); + + VIRTUAL + ~FATDB_SA( + ); + + NONVIRTUAL + BOOLEAN + Initialize( + IN OUT PLOG_IO_DP_DRIVE Drive, + IN OUT PMESSAGE Message, + IN BOOLEAN Formatted DEFAULT TRUE + ); + + NONVIRTUAL + BOOLEAN + Create( + IN PCNUMBER_SET BadSectors, + IN OUT PMESSAGE Message, + IN PCWSTRING Label DEFAULT NULL, + IN ULONG ClusterSize DEFAULT 0, + IN ULONG VirtualSize DEFAULT 0 + ); + + NONVIRTUAL + BOOLEAN + Read( + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + Write( + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + PCVF_FAT_EXTENS + GetFatExtensions( + ); + + NONVIRTUAL + USHORT + QuerySectorsPerCluster( + ) CONST; + + NONVIRTUAL + USHORT + QuerySectorsPerFat( + ) CONST; + + NONVIRTUAL + USHORT + QueryFats( + ) CONST; + + NONVIRTUAL + PARTITION_SYSTEM_ID + QuerySystemId( + ) CONST; + + NONVIRTUAL + LBN + QueryStartDataLbn( + ) CONST; + + NONVIRTUAL + USHORT + QueryClusterCount( + ) CONST; + + NONVIRTUAL + SECTORCOUNT + QueryFreeSectors( + ) CONST; + + NONVIRTUAL + FATTYPE + QueryFatType( + ) CONST; + + NONVIRTUAL + BYTE + QueryVolumeFlags( + ) CONST; + + NONVIRTUAL + VOID + SetVolumeFlags( + BYTE Flags, + BOOLEAN ResetFlags + ); + + NONVIRTUAL + BOOLEAN + QueryCensusAndRelocate ( + OUT PCENSUS_REPORT CensusReport DEFAULT NULL, + IN OUT PINTSTACK RelocationStack DEFAULT NULL, + OUT PBOOLEAN Relocated DEFAULT NULL + ); + + STATIC + USHORT + ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ); + + NONVIRTUAL + BOOLEAN + IsCompressed( + ) CONST; + + NONVIRTUAL + BOOLEAN + ReadSectorZero( + ); + + NONVIRTUAL + ULONG + QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + IsClusterCompressed( + IN ULONG Cluster + ) CONST; + + NONVIRTUAL + VOID + SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN fCompressed + ); + + NONVIRTUAL + UCHAR + QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ); + + NONVIRTUAL + BOOLEAN + VerifyFatExtensions( + IN FIX_LEVEL Fixlevel, + IN PMESSAGE Message, + IN OUT PBOOLEAN pfNeedMsg + ); + + // + // Routines related to the sector heap bitmap. + // + + NONVIRTUAL + BOOLEAN + CheckSectorHeapAllocation( + IN FIX_LEVEL Fixlevel, + IN PMESSAGE Message, + IN OUT PBOOLEAN pfNeedMsg + ); + + NONVIRTUAL + BOOLEAN + FreeClusterData( + ULONG Cluster + ); + + NONVIRTUAL + BOOLEAN + AllocateClusterData( + ULONG Cluster, + UCHAR NumSectors, + BOOLEAN bCompressed, + UCHAR PlainSize + ); + + NONVIRTUAL + BOOLEAN + SetCvfSectorCount( + IN ULONG SectorCount + ); + + private: + + HMEM _mem; // memory for SECRUN + USHORT _ClusterCount; // number of clusters in Super Area + PARTITION_SYSTEM_ID _sysid; // system id + ULONG _sec_per_boot; // sectors for boot code. + + CVF_HEADER _cvf_header; // BPB + dblspace stuff + PCVF_FAT_EXTENS _cvf_extens; // fat extensions (mdfat) + EXTENDED_BIOS_PARAMETER_BLOCK + _dos_exbpb; + + // + // This pointer tells us where the packed extended bpb + // resides in the secrun. + // + + PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK + _pexbpb; + + // _fat is inherited from FAT_SA + // _dir is inherited from FAT_SA + + LBN _StartDataLbn; // LBN of files, or data area + PUCHAR _sector_sig; // sector signature, _cvf_header + PUCHAR _sector_sig2; // same but for _dos_exbpb + + BITVECTOR _sector_heap_bitmap; + BOOLEAN _sector_heap_init; + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + NONVIRTUAL + BOOLEAN + SetBpb( + ); + + NONVIRTUAL + BOOLEAN + SetExtendedBpb( + ); + + NONVIRTUAL + BOOLEAN + DupFats( + ); + + NONVIRTUAL + USHORT + ComputeRootEntries( + ) CONST; + + NONVIRTUAL + ULONG + SecPerBoot( + ); + + NONVIRTUAL + VOLID + QueryVolId( + ) CONST; + + NONVIRTUAL + VOLID + SetVolId( + IN VOLID VolId + ); + + NONVIRTUAL + UCHAR + QueryMediaByte( + ) CONST; + + VIRTUAL + VOID + SetMediaByte( + UCHAR MediaByte + ); + + NONVIRTUAL + BOOLEAN + VerifyBootSector( + ); + + NONVIRTUAL + ULONG + QueryVirtualSectors( + ) CONST; + + NONVIRTUAL + BOOLEAN + CreateBootSector( + ); + + NONVIRTUAL + BOOLEAN + SetOemData( + ); + + NONVIRTUAL + BOOLEAN + SetSignature( + ); + + NONVIRTUAL + BOOLEAN + SetBootSignature( + IN UCHAR Signature DEFAULT sigBOOTSTRAP + ); + + NONVIRTUAL + BOOLEAN + SetBootCode( + ); + + NONVIRTUAL + BOOLEAN + DosSaInit( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN SECTORCOUNT NumberOfSectors, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + SetPhysicalDriveType( + IN PHYSTYPE PhysType + ); + + NONVIRTUAL + BOOLEAN + RecoverChain( + IN OUT PUSHORT StartingCluster, + OUT PBOOLEAN ChangesMade, + IN USHORT EndingCluster DEFAULT 0, + IN BOOLEAN Replace DEFAULT FALSE + ); +}; + +INLINE +USHORT +FATDB_SA::QuerySectorsPerCluster( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors per cluster for + the volume. + +Arguments: + + None. + +Return Value: + + The number of sectors per cluster for the volume. + +--*/ +{ + return _cvf_header.Bpb.SectorsPerCluster ? + _cvf_header.Bpb.SectorsPerCluster : 256; +} + + +INLINE +USHORT +FATDB_SA::QuerySectorsPerFat( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors per FAT for the volume. + +Arguments: + + None. + +Return Value: + + The number of sectors per FAT for the volume. + +--*/ +{ + return _cvf_header.Bpb.SectorsPerFat; +} + + +INLINE +USHORT +FATDB_SA::QueryFats( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of FATs on the volume. + +Arguments: + + None. + +Return Value: + + Doublespace drives always have just a single FAT. + +--*/ +{ + return 1; +} + + +INLINE +PARTITION_SYSTEM_ID +FATDB_SA::QuerySystemId( + ) CONST +/*++ + +Routine Description: + + This routine computes the system ID for the volume. + +Arguments: + + None. + +Return Value: + + The system ID for the volume. + +--*/ +{ + return _sysid; +} + + +INLINE +LBN +FATDB_SA::QueryStartDataLbn( + ) CONST +/*++ + +Routine Description: + + This routine returns the LBN of the first logical cluster of the + volume. + +Arguments: + + None. + +Return Value: + + The LBN of the first logical cluster of the volume. + +--*/ +{ + return _StartDataLbn; +} + + +INLINE +USHORT +FATDB_SA::QueryClusterCount( + ) CONST +/*++ + +Routine Description: + + This routine computes the total number of clusters for the volume. + That is to say that the largest addressable cluster on the disk + is cluster number 'QueryClusterCount() - 1'. Note that the + smallest addressable cluster on the disk is 2. + +Arguments: + + None. + +Return Value: + + The total number of clusters for the volume. + +--*/ +{ + return _ClusterCount; +} + +INLINE +BOOLEAN +FATDB_SA::IsCompressed( + ) CONST +/*++ + +Routine Description: + + This routine always returns TRUE for DblSpace volumes. Comparable + classes for non-dblspace volumes will return FALSE. + +Arguments: + +Return Value: + + TRUE - Compressed. + FALSE - Not compressed. + +--*/ +{ + return TRUE; +} + +INLINE +BOOLEAN +FATDB_SA::ReadSectorZero( + ) +/*++ + +Routine Description: + + This routine used to be DOS_SUPERAREA::Read(). + +Arguments: + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + BOOLEAN f; + PPACKED_CVF_HEADER ph; + + f = SECRUN::Read(); + if (!f) + return f; + + ph = (PPACKED_CVF_HEADER)SECRUN::GetBuf(); + + CvfUnpackCvfHeader(&_cvf_header, ph); + return TRUE; +} + +INLINE +UCHAR +FATDB_SA::QueryMediaByte( + ) CONST +/*++ + +Routine Description: + + This routine fetches the media byte from the super area's data. + +Arguments: + + None. + +Return Value: + + The media byte residing in the super area. + +--*/ +{ + return _cvf_header.Bpb.Media; +} + +INLINE +VOID +FATDB_SA::SetMediaByte( + UCHAR MediaByte + ) +/*++ + +Routine Description: + + This routine sets the media byte in the super area's data. + +Arguments: + + MediaByte -- Supplies the new media byte. + +Return Value: + + None. + +--*/ +{ + _cvf_header.Bpb.Media = MediaByte; +} + +INLINE +SECTORCOUNT +FATDB_SA::QueryVirtualSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors on the volume according + to the file system. + +Arguments: + + None. + +Return Value: + + The number of sectors on the volume according to the file system. + +--*/ +{ + return _cvf_header.Bpb.LargeSectors; +} + +INLINE +VOLID +FATDB_SA::QueryVolId( + ) CONST +/*++ + +Routine Description: + + This routine fetches the volume ID from the super area's data. + This routine will return 0 if volume serial numbers are not + supported by the partition. + +Arguments: + + None. + +Return Value: + + The volume ID residing in the super area. + +--*/ +{ + return (_dos_exbpb.Signature == 0x28 || _dos_exbpb.Signature == 0x29) + ? _dos_exbpb.SerialNumber : 0; +} + +INLINE +VOLID +FATDB_SA::SetVolId( + IN VOLID VolId + ) +/*++ + +Routine Description: + + This routine does nothing; volume serial numbers are not supported + by FATDB. + +Arguments: + + VolId - The new volume ID. + +Return Value: + + The VolId. + +--*/ +{ + return _dos_exbpb.SerialNumber = VolId; +} + +INLINE +BOOLEAN +FATDB_SA::SetBootSignature( + IN UCHAR Signature + ) +/*++ + +Routine Description: + + This routine sets the boot signature in the dos boot sector. + +Arguments: + + Signature -- supplies the character to set the signature to. + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + _dos_exbpb.Signature = Signature; + return TRUE; +} + +INLINE +BYTE +FATDB_SA::QueryVolumeFlags( + ) CONST +/*++ + +Routine Description: + + This routine returns the volume flags byte from the bpb. + +Arguments: + + None. + +Return Value: + + The flags. + +--*/ +{ + return _cvf_header.Bpb.CurrentHead; +} + +INLINE +VOID +FATDB_SA::SetVolumeFlags( + BYTE Flags, + BOOLEAN ResetFlags + ) +/*++ + +Routine Description: + + This routine sets the volume flags in the bpb. + +Arguments: + + Flags -- flags to set + ResetFlags -- if true, Flags are cleared instead of set + +Return Value: + + None. + +--*/ +{ + if (ResetFlags) { + _cvf_header.Bpb.CurrentHead &= ~Flags; + } else { + _cvf_header.Bpb.CurrentHead |= Flags; + } +} + +#endif // FATDB_SA_DEFN diff --git a/private/utils/ufat/inc/fatdbvol.hxx b/private/utils/ufat/inc/fatdbvol.hxx new file mode 100644 index 000000000..7eb7632d4 --- /dev/null +++ b/private/utils/ufat/inc/fatdbvol.hxx @@ -0,0 +1,97 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + dbvol.hxx + +Abstract: + + This class implements Double Space only VOLUME items. + +Author: + + Bill McJohn (billmc) 23-September 1993 + +--*/ + +#if !defined(_FATDB_VOL_DEFN_) + +#define _FATDB_VOL_DEFN_ + +#include "volume.hxx" +#include "fatdbsa.hxx" + +// +// Forward references +// + +DECLARE_CLASS( FATDB_VOL ); +DECLARE_CLASS( MESSAGE ); + + +class FATDB_VOL : public VOL_LIODPDRV { + + public: + + DECLARE_CONSTRUCTOR( FATDB_VOL ); + + VIRTUAL + ~FATDB_VOL( + ); + + NONVIRTUAL + BOOLEAN + Initialize( + IN PCWSTRING NtDriveName, + IN PCWSTRING HostFileName, + IN OUT PMESSAGE Message DEFAULT NULL, + IN BOOLEAN ExclusiveWrite DEFAULT FALSE + ); + + NONVIRTUAL + BOOLEAN + IsFileContiguous( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message DEFAULT NULL, + OUT PULONG NumBlocks DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + ContiguityReport( + IN PCWSTRING DirectoryPath, + IN PCDSTRING FilesToCheck, + IN ULONG NumberOfFiles, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + PVOL_LIODPDRV + QueryDupVolume( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message DEFAULT NULL, + IN BOOLEAN ExclusiveWrite DEFAULT FALSE, + IN BOOLEAN FormatMedia DEFAULT FALSE, + IN MEDIA_TYPE MediaType DEFAULT Unknown + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + FATDB_SA _fatdbsa; + +}; + + +#endif // _FATDB_VOL_DEFN_ diff --git a/private/utils/ufat/inc/fatdent.hxx b/private/utils/ufat/inc/fatdent.hxx new file mode 100644 index 000000000..5364c5b00 --- /dev/null +++ b/private/utils/ufat/inc/fatdent.hxx @@ -0,0 +1,1052 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatdent.hxx + +Abstract: + + This class models a FAT directory entry. + +Author: + + Norbert P. Kusters (norbertk) 4-Dec-90 + +--*/ + +#if !defined(FAT_DIRENT_DEFN) + +#define FAT_DIRENT_DEFN + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +DECLARE_CLASS( FAT_DIRENT ); +DECLARE_CLASS( WSTRING ); +DECLARE_CLASS( WSTRING ); +DECLARE_CLASS( TIMEINFO ); + +typedef struct _SHORT_FAT_DIRENT { + + UCHAR Name[11]; + UCHAR Attributes[1]; + UCHAR NtByte[1]; + UCHAR CreationMSec[1]; // actually count of 10msec's + UCHAR CreationTime[2]; // two-second resolution + UCHAR CreationDate[2]; + UCHAR LastAccessDate[2]; + UCHAR EaHandle[2]; + UCHAR LastWriteTime[2]; + UCHAR LastWriteDate[2]; + UCHAR FirstCluster[2]; + UCHAR Size[4]; +}; + +DEFINE_TYPE( _SHORT_FAT_DIRENT, SHORT_FAT_DIRENT ); + +typedef struct _LONG_FAT_DIRENT { + + UCHAR Ordinal[1]; + UCHAR Name1[10]; + UCHAR Attribute[1]; + UCHAR Type[1]; + UCHAR Checksum[1]; + UCHAR Name2[12]; + UCHAR FirstCluster[2]; + UCHAR Name3[4]; +}; + +DEFINE_TYPE( _LONG_FAT_DIRENT, LONG_FAT_DIRENT ); + +#define LONG_DIRENT_TYPE_NAME 0 +#define LONG_DIRENT_TYPE_CLASS 1 + +class FAT_DIRENT : public OBJECT { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR( FAT_DIRENT ); + + VIRTUAL + UFAT_EXPORT + ~FAT_DIRENT( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PVOID Dirent + ); + + NONVIRTUAL + VOID + Clear( + ); + + NONVIRTUAL + UFAT_EXPORT + VOID + QueryName( + OUT PWSTRING Name + ) CONST; + + NONVIRTUAL + BOOLEAN + SetName( + IN PCWSTRING Name + ); + + NONVIRTUAL + BOOLEAN + IsValidName( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsDot( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsDotDot( + ) CONST; + + NONVIRTUAL + BYTE + QueryAttributeByte( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + IsValidLastWriteTime( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + QueryLastWriteTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST; + + NONVIRTUAL + BOOLEAN + SetLastWriteTime( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + IsValidCreationTime( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + QueryCreationTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST; + + NONVIRTUAL + BOOLEAN + SetCreationTime( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + IsValidLastAccessTime( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + QueryLastAccessTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST; + + NONVIRTUAL + BOOLEAN + SetLastAccessTime( + ); + + NONVIRTUAL + USHORT + QueryStartingCluster( + ) CONST; + + NONVIRTUAL + VOID + SetStartingCluster( + IN USHORT NewStartingCluster + ); + + NONVIRTUAL + ULONG + QueryFileSize( + ) CONST; + + NONVIRTUAL + VOID + SetFileSize( + IN ULONG NewFileSize + ); + + NONVIRTUAL + USHORT + QueryEaHandle( + ) CONST; + + NONVIRTUAL + VOID + SetEaHandle( + IN USHORT NewHandle + ); + + NONVIRTUAL + BOOLEAN + IsEndOfDirectory( + ) CONST; + + NONVIRTUAL + VOID + SetEndOfDirectory( + ); + + NONVIRTUAL + BOOLEAN + IsErased( + ) CONST; + + NONVIRTUAL + VOID + SetErased( + ); + + NONVIRTUAL + BOOLEAN + IsHidden( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsSystem( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsVolumeLabel( + ) CONST; + + NONVIRTUAL + VOID + SetVolumeLabel( + ); + + NONVIRTUAL + BOOLEAN + IsDirectory( + ) CONST; + + NONVIRTUAL + VOID + SetDirectory( + ); + + NONVIRTUAL + VOID + ResetDirectory( + ); + + NONVIRTUAL + UCHAR + QueryChecksum( + ) CONST; + + NONVIRTUAL + BOOLEAN + Is8LowerCase( + ) CONST; + + + NONVIRTUAL + BOOLEAN + Is3LowerCase( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsLongEntry( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsLongNameEntry( + ) CONST; + + NONVIRTUAL + BOOLEAN + QueryLongOrdinal( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsLastLongEntry( + ) CONST; + + NONVIRTUAL + BOOLEAN + IsWellTerminatedLongNameEntry( + ) CONST; + + NONVIRTUAL + BOOLEAN + QueryLongNameComponent( + OUT PWSTRING NameComponent + ) CONST; + + NONVIRTUAL + BOOLEAN + NameHasTilde( + ) CONST; + + + NONVIRTUAL + BOOLEAN + NameHasExtendedChars( + ) CONST; + + private: + + NONVIRTUAL + BOOLEAN + TimeStampsAreValid( + USHORT t, + USHORT d + ) CONST; + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + PUCHAR _dirent; + +}; + + +INLINE +VOID +FAT_DIRENT::Clear( + ) +/*++ + +Routine Description: + + This routine zeros out the directory entry. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + memset(_dirent, 0, 32); +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsDot( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the directory entry is the "." + entry. + +Arguments: + + None. + +Return Value: + + FALSE - The entry is not the "." entry. + TRUE - The entry is the "." entry. + +--*/ +{ + return !memcmp(_dirent, ". ", 11); +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsDotDot( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the directory entry is the ".." + entry. + +Arguments: + + None. + +Return Value: + + FALSE - The entry is not the ".." entry. + TRUE - The entry is the ".." entry. + +--*/ +{ + return !memcmp(_dirent, ".. ", 11); +} + + +INLINE +USHORT +FAT_DIRENT::QueryStartingCluster( + ) CONST +/*++ + +Routine Description: + + This routine computes the starting cluster number of the directory entry. + +Arguments: + + None. + +Return Value: + + The starting cluster number of the directory entry. + +--*/ +{ + DebugAssert(_dirent); + return *((PUSHORT) &_dirent[26]); +} + + +INLINE +VOID +FAT_DIRENT::SetStartingCluster( + IN USHORT NewStartingCluster + ) +/*++ + +Routine Description: + + This routine sets the starting cluster number for the directory entry. + +Arguments: + + NewStartingCluster - Supplies the starting cluster number for the + directory entry. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + *((PUSHORT) &_dirent[26]) = NewStartingCluster; +} + + +INLINE +ULONG +FAT_DIRENT::QueryFileSize( + ) CONST +/*++ + +Routine Description: + + This routine returns the number of bytes in the file. + +Arguments: + + None. + +Return Value: + + The number of bytes in the file. + +--*/ +{ + DebugAssert(_dirent); + return *((PULONG) &_dirent[28]); +} + + +INLINE +VOID +FAT_DIRENT::SetFileSize( + IN ULONG NewFileSize + ) +/*++ + +Routine Description: + + This routine sets the file size in the directory entry. + +Arguments: + + NewFileSize - Supplies the new file size. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + *((PULONG) &_dirent[28]) = NewFileSize; +} + + +INLINE +USHORT +FAT_DIRENT::QueryEaHandle( + ) CONST +/*++ + +Routine Description: + + This routine returns the EA handle for the file. + +Arguments: + + None. + +Return Value: + + The EA handle for the file. + +--*/ +{ + DebugAssert(_dirent); + return *((PUSHORT) &_dirent[20]); +} + + +INLINE +VOID +FAT_DIRENT::SetEaHandle( + IN USHORT NewHandle + ) +/*++ + +Routine Description: + + This routine sets the EA handle for the file. + +Arguments: + + NewHandle - Supplies the EA handle for the file. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + *((PUSHORT) &_dirent[20]) = NewHandle; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsEndOfDirectory( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not this directory entry marks + the end of the directory. + +Arguments: + + None. + +Return Value: + + FALSE - This entry does not mark the end of the directory. + TRUE - This entry marks the end of the directory. + +--*/ +{ + DebugAssert(_dirent); + return _dirent[0] ? FALSE : TRUE; +} + + +INLINE +VOID +FAT_DIRENT::SetEndOfDirectory( + ) +/*++ + +Routine Description: + + This routine sets this directory entry marks to the end of the + directory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + _dirent[0] = 0; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsErased( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the directory entry is erased or not. + +Arguments: + + None. + +Return Value: + + FALSE - The directory entry is not erased. + TRUE - The directory entry is erased. + +--*/ +{ + DebugAssert(_dirent); + return _dirent[0] == 0xE5 ? TRUE : FALSE; +} + + +INLINE +VOID +FAT_DIRENT::SetErased( + ) +/*++ + +Routine Description: + + This routine marks the directory entry as erased. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + _dirent[0] = 0xE5; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsHidden( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the file is hidden. + +Arguments: + + None. + +Return Value: + + FALSE - The file is not hidden. + TRUE - The file is hidden. + +--*/ +{ + DebugAssert(_dirent); + return _dirent[11]&0x02 ? TRUE : FALSE; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsSystem( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the file is a system file. + +Arguments: + + None. + +Return Value: + + FALSE - The file is not a system file. + TRUE - The file is a system file. + +--*/ +{ + DebugAssert(_dirent); + return _dirent[11]&0x04 ? TRUE : FALSE; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsVolumeLabel( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the first 11 characters of the + directory entry are the volume label or not. + +Arguments: + + None. + +Return Value: + + FALSE - The directory entry is not a volume label. + TRUE - The directory entry is a volume label. + +--*/ +{ + DebugAssert(_dirent); + return ((_dirent[11]&0x08) && !IsLongEntry()); +} + +INLINE +BOOLEAN +FAT_DIRENT::IsLongEntry( + ) CONST +/*++ + +Routine Description; + + This routine determines whether this entry is a + Long Directory Entry. + + The entry is a Long Directory Entry if the attribute + field has all four low-order bits (read-only, hidden, + system, and volume-label) set. The four high-order + bits are ignored. + +Arguments: + + None. + +Return Value: + + TRUE if the entry is a Long Name Directory Entry. + +--*/ +{ + return( (_dirent[11] & 0xF) == 0xF ); +} + +INLINE +BOOLEAN +FAT_DIRENT::IsLongNameEntry( + ) CONST +/*++ + +Routine Description; + + This routine determines whether this entry is a + Long Name Directory Entry. + + A Long Name directory entry is a Long Directory Entry + with a type field of LONG_DIRENT_TYPE_LONG_NAME. + +Arguments: + + None. + +Return Value: + + TRUE if the entry is a Long Name Directory Entry. + +--*/ +{ + return( IsLongEntry() && (_dirent[12] == LONG_DIRENT_TYPE_NAME) ); +} + +INLINE +BOOLEAN +FAT_DIRENT::QueryLongOrdinal( + ) CONST +/*++ + +Routine Description; + + This method returns the ordinal of a long directory entry. + If the directory entry is not a long entry, it returns + 0xFF. Note that this method strips off the Last Long Entry + flag before returning the ordinal. To determine if an entry + is the last of a set of long entries, call IsLastLongEntry. + +Arguments: + + None. + +Return Value: + + The ordinal of this entry. + +--*/ +{ + return( IsLongEntry() ? _dirent[0] & 0x3F: 0xFF ); +} + +INLINE +BOOLEAN +FAT_DIRENT::IsLastLongEntry( + ) CONST +/*++ + +Routine Description: + + This method determines whether this entry is the last + of a set of long directory entries. + +Arguments: + + None. + +Return Value: + + TRUE if this is the last of a set of long directory entries. + +--*/ +{ + return( IsLongEntry() ? _dirent[0] & 0x40 : FALSE ); +} + + +INLINE +VOID +FAT_DIRENT::SetVolumeLabel( + ) +/*++ + +Routine Description: + + This routine sets the directory entry to be a volume label. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + _dirent[11] |= 0x08; +} + + +INLINE +BOOLEAN +FAT_DIRENT::IsDirectory( + ) CONST +/*++ + +Routine Description: + + This routine computes whether or not the directory entry is a directory. + +Arguments: + + None. + +Return Value: + + FALSE - The directory entry is not a directory. + TRUE - The directory entry is a directory. + +--*/ +{ + DebugAssert(_dirent); + return ((_dirent[11]&0x10) && !IsLongEntry()); +} + + +INLINE +VOID +FAT_DIRENT::SetDirectory( + ) +/*++ + +Routine Description: + + This routine sets the directory entry to be a directory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + _dirent[11] |= 0x10; +} + + +INLINE +VOID +FAT_DIRENT::ResetDirectory( + ) +/*++ + +Routine Description: + + This routine sets the directory entry to not be a directory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DebugAssert(_dirent); + _dirent[11] &= ~0x10; +} + + + + +INLINE +BYTE +FAT_DIRENT::QueryAttributeByte( + ) CONST +/*++ + +Routine Description: + + This routine returns the attribute byte of the directory entry + +Arguments: + + None. + +Return Value: + + Attribute byte + +--*/ +{ + DebugAssert(_dirent); + return _dirent[11]; +} + + +INLINE +BOOLEAN +FAT_DIRENT::Is8LowerCase( + ) CONST +/*++ + +Routine Description: + + This routine tells whether the first 8 bytes of the short name + should be downcased after being read from the disk. + +Arguments: + + None. + +Return Value: + + TRUE or FALSE + +--*/ +{ + + DebugAssert(_dirent); + return (_dirent[12] & 0x08) != 0; +} + + +INLINE +BOOLEAN +FAT_DIRENT::Is3LowerCase( + ) CONST +/*++ + +Routine Description: + + This routine tells whether the last 8 bytes of the short name + should be downcased after being read from the disk. + +Arguments: + + None. + +Return Value: + + TRUE or FALSE + +--*/ +{ + DebugAssert(_dirent); + return (_dirent[12] & 0x10) != 0; +} + +#endif // FAT_DIRENT_DEFN diff --git a/private/utils/ufat/inc/fatdir.hxx b/private/utils/ufat/inc/fatdir.hxx new file mode 100644 index 000000000..e38f1442b --- /dev/null +++ b/private/utils/ufat/inc/fatdir.hxx @@ -0,0 +1,90 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatdir.hxx + +Abstract: + + This class is a virtual template for a FAT directory. It will be + passed to functions who wish to query the directory entries from the + directory without knowledge of how or where the directory is stored. + + The user of this class will not be able to read or write the + directory to disk. + +Author: + + Norbert P. Kusters (norbertk) 4-Dec-90 + +--*/ + +#if !defined(FATDIR_DEFN) + +#define FATDIR_DEFN + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +DECLARE_CLASS( FATDIR ); +DECLARE_CLASS( WSTRING ); +DEFINE_POINTER_TYPES( PFATDIR ); + + +CONST BytesPerDirent = 32; + + +class FATDIR : public OBJECT { + + public: + + VIRTUAL + PVOID + GetDirEntry( + IN LONG EntryNumber + ) PURE; + + NONVIRTUAL + UFAT_EXPORT + PVOID + SearchForDirEntry( + IN PCWSTRING FileName + ); + + NONVIRTUAL + PVOID + GetFreeDirEntry( + ); + + VIRTUAL + BOOLEAN + Read( + ) PURE; + + VIRTUAL + BOOLEAN + Write( + ) PURE; + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + QueryLongName( + IN LONG EntryNumber, + OUT PWSTRING LongName + ); + + protected: + + DECLARE_CONSTRUCTOR( FATDIR ); + +}; + +#endif // FATDIR_DEFN diff --git a/private/utils/ufat/inc/fatsa.hxx b/private/utils/ufat/inc/fatsa.hxx new file mode 100644 index 000000000..1f717feac --- /dev/null +++ b/private/utils/ufat/inc/fatsa.hxx @@ -0,0 +1,754 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatsa.hxx + +Abstract: + + +Author: + + Matthew Bradburn (mattbr) 1-Oct-93 + +--*/ + +#ifndef FATSUPERA_DEFN +#define FATSUPERA_DEFN + +#include "hmem.hxx" +#include "supera.hxx" +#include "message.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( ARRAY ); +DECLARE_CLASS( BITVECTOR ); +DECLARE_CLASS( EA_HEADER ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( FAT_DIRENT ); +DECLARE_CLASS( FATDIR ); +DECLARE_CLASS( GENERIC_STRING ); +DECLARE_CLASS( INTSTACK ); +DECLARE_CLASS( NUMBER_SET ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); +DECLARE_CLASS( MESSAGE ); +DECLARE_CLASS( ROOTDIR ); +DECLARE_CLASS( SORTED_LIST ); +DECLARE_CLASS( TIMEINFO ); +DECLARE_CLASS( WSTRING ); +DEFINE_POINTER_TYPES( PFATDIR ); + + +enum FATTYPE { + SMALL, // 12 bit fat + LARGE // 16 bit fat +}; + +// the text for the oem data field +#define OEMTEXT "MSDOS5.0" +#define OEMTEXTLENGTH 8 + +#define sigBOOTSTRAP (UCHAR)0x29 // boot strap signature + +CONST MaxSecPerClus = 128; // The maximum number of sectors per cluster. + +struct _EA_INFO { + USHORT OwnHandle; + USHORT PreceedingCn; // Clus num preceeding first cluster of set. + USHORT LastCn; // The number of the last cluster in the set. + STR OwnerFileName[14]; // Owner file name as found in ea set. + UCHAR UsedCount; // Number of files using ea set. + STR UserFileName[14]; // File name of ea set user. + USHORT UserFileEntryCn; // Clus num of directory for file. + ULONG UserFileEntryNumber; // Dirent num for file name. +}; + +DEFINE_TYPE( struct _EA_INFO, EA_INFO ); + +struct _FATCHK_REPORT { + ULONG HiddenEntriesCount; + USHORT HiddenClusters; + ULONG FileEntriesCount; + USHORT FileClusters; + ULONG DirEntriesCount; + USHORT DirClusters; + ULONG ExitStatus; +}; + +DEFINE_TYPE( struct _FATCHK_REPORT, FATCHK_REPORT ); + + +struct _CENSUS_REPORT { + ULONG FileEntriesCount; + USHORT FileClusters; + ULONG DirEntriesCount; + USHORT DirClusters; + USHORT EaClusters; +}; + +DEFINE_TYPE( struct _CENSUS_REPORT, CENSUS_REPORT ); + + +class FAT_SA : public SUPERAREA { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR(FAT_SA); + + VIRTUAL + UFAT_EXPORT + ~FAT_SA( + ); + + VIRTUAL + BOOLEAN + Initialize( + IN OUT PLOG_IO_DP_DRIVE Drive, + IN OUT PMESSAGE Message, + IN BOOLEAN Formatted + ) PURE; + + VIRTUAL + BOOLEAN + Create( + IN PCNUMBER_SET BadSectors, + IN OUT PMESSAGE Message, + IN PCWSTRING Label DEFAULT NULL, + IN ULONG ClusterSize DEFAULT 0, + IN ULONG VirtualSize DEFAULT 0 + ) PURE; + + NONVIRTUAL + BOOLEAN + VerifyAndFix( + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN BOOLEAN Verbose DEFAULT FALSE, + IN BOOLEAN OnlyIfDirty DEFAULT FALSE, + IN BOOLEAN RecoverFree DEFAULT FALSE, + IN BOOLEAN RecoverAlloc DEFAULT FALSE, + IN BOOLEAN Resize DEFAULT FALSE, + IN ULONG LogFileSize DEFAULT 0, + OUT PULONG ExitStatus DEFAULT NULL, + IN PCWSTRING DriveLetter DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + RecoverFile( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + Read( + ); + + VIRTUAL + BOOLEAN + Read( + IN OUT PMESSAGE Message + ) PURE; + + NONVIRTUAL + BOOLEAN + Write( + ); + + VIRTUAL + BOOLEAN + Write( + IN OUT PMESSAGE Message + ) PURE; + + NONVIRTUAL + PFAT + GetFat( + ); + + NONVIRTUAL + PROOTDIR + GetRootDir( + ); + + VIRTUAL + USHORT + QuerySectorsPerCluster( + ) CONST PURE; + + + VIRTUAL + USHORT + QuerySectorsPerFat( + ) CONST PURE; + + VIRTUAL + ULONG + QueryVirtualSectors( + ) CONST PURE; + + VIRTUAL + USHORT + QueryFats( + ) CONST PURE; + + VIRTUAL + PARTITION_SYSTEM_ID + QuerySystemId( + ) CONST PURE; + + VIRTUAL + LBN + QueryStartDataLbn( + ) CONST PURE; + + VIRTUAL + USHORT + QueryClusterCount( + ) CONST PURE; + + NONVIRTUAL + SECTORCOUNT + QueryFreeSectors( + ) CONST; + + NONVIRTUAL + FATTYPE + QueryFatType( + ) CONST; + + VIRTUAL + BYTE + QueryVolumeFlags( + ) CONST PURE; + + VIRTUAL + VOID + SetVolumeFlags( + BYTE Flags, + BOOLEAN ResetFlags + ) PURE; + + VIRTUAL + BOOLEAN + RecoverChain( + IN OUT PUSHORT StartingCluster, + OUT PBOOLEAN ChangesMade, + IN USHORT EndingCluster DEFAULT 0, + IN BOOLEAN Replace DEFAULT FALSE + ) PURE; + + VIRTUAL + BOOLEAN + QueryLabel( + OUT PWSTRING Label + ) CONST; + + NONVIRTUAL + BOOLEAN + QueryLabel( + OUT PWSTRING Label, + OUT PTIMEINFO TimeInfo + ) CONST; + + NONVIRTUAL + BOOLEAN + SetLabel( + IN PCWSTRING NewLabel + ); + + NONVIRTUAL + UFAT_EXPORT + USHORT + QueryFileStartingCluster( + IN PCWSTRING FullPathFileName, + OUT PHMEM Hmem DEFAULT NULL, + OUT PPFATDIR Directory DEFAULT NULL, + OUT PBOOLEAN DeleteDirectory DEFAULT NULL, + OUT PFAT_DIRENT DirEntry DEFAULT NULL + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + QueryCensusAndRelocate ( + OUT PCENSUS_REPORT CensusReport DEFAULT NULL, + IN OUT PINTSTACK RelocationStack DEFAULT NULL, + OUT PBOOLEAN Relocated DEFAULT NULL + ); + + STATIC + USHORT + ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ); + + VIRTUAL + BOOLEAN + IsCompressed( + ) CONST PURE; + + VIRTUAL + BOOLEAN + ReadSectorZero( + ) PURE; + + STATIC BOOLEAN + FAT_SA::IsValidString( + IN PCWSTRING String + ); + + // + // These routines are used to access the CVF_EXTENSIONS on + // FATDB, and they do the minimal thing on REAL_FAT. + // + + VIRTUAL + ULONG + QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors DEFAULT NULL + ) PURE; + + VIRTUAL + BOOLEAN + IsClusterCompressed( + IN ULONG Cluster + ) CONST PURE; + + VIRTUAL + VOID + SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN fCompressed + ) PURE; + + VIRTUAL + UCHAR + QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ) PURE; + + // + // These routines are used to manage the sector heap for + // FATDB, and do nothing on REAL_FAT. + // + + VIRTUAL + BOOLEAN + FreeClusterData( + IN ULONG Cluster + ) PURE; + + VIRTUAL + BOOLEAN + AllocateClusterData( + IN ULONG Cluster, + IN UCHAR NumSectors, + IN BOOLEAN bCompressed, + IN UCHAR PlainSize + ) PURE; + + protected: + + PFAT _fat; // Pointer to FAT; + FATTYPE _ft; // fat type required by area + PROOTDIR _dir; // Pointer to Root directory + + VIRTUAL + BOOLEAN + SetBpb( + ) PURE; + + VIRTUAL + ULONG + SecPerBoot( + ) PURE; + + VIRTUAL + VOLID + QueryVolId( + ) CONST PURE; + + VIRTUAL + VOLID + SetVolId( + IN VOLID VolId + ) PURE; + + VIRTUAL + UCHAR + QueryMediaByte( + ) CONST PURE; + + VIRTUAL + VOID + SetMediaByte( + UCHAR MediaByte + ) PURE; + + NONVIRTUAL + PARTITION_SYSTEM_ID + ComputeSystemId( + ) CONST; + + NONVIRTUAL + FATTYPE + ComputeFatType( + ) CONST; + + NONVIRTUAL + BOOLEAN + RecoverOrphans( + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + VIRTUAL + BOOLEAN + VerifyFatExtensions( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN PBOOLEAN pfNeedMsg + ) PURE; + + VIRTUAL + BOOLEAN + CheckSectorHeapAllocation( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN PBOOLEAN pfNeedMsg + ) PURE; + + private: + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + NONVIRTUAL + USHORT + ComputeRootEntries( + ) CONST; + + NONVIRTUAL + BOOLEAN + PerformEaLogOperations( + IN USHORT EaFileCn, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + PEA_INFO + RecoverEaSets( + IN USHORT EaFileCn, + OUT PUSHORT NumEas, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + USHORT + VerifyAndFixEaSet( + IN USHORT PreceedingCluster, + OUT PEA_INFO EaInfo, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + EaSort( + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + RebuildEaHeader( + IN OUT PUSHORT StartingCluster, + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PMEM EaHeaderMem, + OUT PEA_HEADER EaHeader, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + WalkDirectoryTree( + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PBITVECTOR FatBitMap, + OUT PFATCHK_REPORT Report, + IN FIX_LEVEL FixLevel, + IN BOOLEAN RecoverAlloc, + IN OUT PMESSAGE Message, + IN BOOLEAN Verbose, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + ValidateDirent( + IN OUT PFAT_DIRENT Dirent, + IN PCWSTRING FilePath, + IN FIX_LEVEL FixLevel, + IN BOOLEAN RecoverAlloc, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage, + IN OUT PBITVECTOR FatBitMap, + OUT PBOOLEAN CrossLinkDetected, + OUT PUSHORT CrossLinkPreviousCluster, + OUT PULONG ExitStatus + ); + + NONVIRTUAL + BOOLEAN + ValidateEaHandle( + IN OUT PFAT_DIRENT Dirent, + IN USHORT DirClusterNumber, + IN ULONG DirEntryNumber, + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN PCWSTRING FilePath, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + CopyClusters( + IN USHORT SourceChain, + OUT PUSHORT DestChain, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + PurgeEaFile( + IN PCEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + BOOLEAN + InitRelocationList( + IN OUT PINTSTACK RelocationStack, + IN OUT PUSHORT RelocatedChain, + IN OUT PSORTED_LIST ClustersToRelocate, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + BOOLEAN + RelocateFirstCluster( + IN OUT PFAT_DIRENT Dirent + ); + + NONVIRTUAL + USHORT + RelocateOneCluster( + IN USHORT Cluster, + IN USHORT Previous + ); + + NONVIRTUAL + BOOLEAN + DoDirectoryCensusAndRelocation( + IN OUT PFATDIR Directory, + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + BOOLEAN + DoVolumeCensusAndRelocation( + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + BOOLEAN + RecoverFreeSpace( + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + AllocSectorsForChain( + IN ULONG StartingCluster + ); +}; + + +INLINE +BOOLEAN +FAT_SA::Read( + ) +/*++ + +Routine Description: + + This routine simply calls the other read with the default message + object. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + MESSAGE msg; + + return Read(&msg); +} + + +INLINE +BOOLEAN +FAT_SA::Write( + ) +/*++ + +Routine Description: + + This routine simply calls the other write with the default message + object. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + MESSAGE msg; + + return Write(&msg); +} + + +INLINE +PFAT +FAT_SA::GetFat( + ) +/*++ + +Routine Description: + + This routine returns a pointer to the FAT maintained by this class. + It is not necessary to read or write this FAT since it shares memory + with the FAT_SA class and thus performing FAT_SA::Read will read in + the FAT and performing FAT_SA::Write will write the FAT. Additionally, + performing a FAT_SA::Write will duplicate the information in the local + FAT object to all other FATs on the disk. + +Arguments: + + None. + +Return Value: + + A pointer to the FAT super area's FAT. + +--*/ +{ + return _fat; +} + + +INLINE +PROOTDIR +FAT_SA::GetRootDir( + ) +/*++ + +Routine Description: + + This routine return a pointer to the FAT super area's root directory. + The memory of this root directory is shared with the FAT super area. + Hence, as with 'GetFat' it is not necessary to read or write the + root directory returned by this routine if a FAT_SA::Read or + FAT_SA::Write is being performed respecively. + +Arguments: + + None. + +Return Value: + + A pointer to the FAT super area's root directory. + +--*/ +{ + return _dir; +} + +extern BOOLEAN +IsValidString( + IN PCWSTRING String + ); + +#endif // FATSUPERA_DEFN + +extern VOID +dofmsg( + IN PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); diff --git a/private/utils/ufat/inc/fatvol.hxx b/private/utils/ufat/inc/fatvol.hxx new file mode 100644 index 000000000..9d043e75f --- /dev/null +++ b/private/utils/ufat/inc/fatvol.hxx @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatvol.hxx + +Abstract: + + This class implements FAT only VOLUME items. + +Author: + + Mark Shavlik (marks) 13-Feb-90 + +--*/ + +#if !defined(FATVOL) + +#define FATVOL + +#include "volume.hxx" +#include "rfatsa.hxx" + +// +// Forward references +// + +DECLARE_CLASS( FAT_VOL ); +DECLARE_CLASS( MESSAGE ); + + +#if ! defined( _SETUP_LOADER_ ) + + +class FAT_VOL : public VOL_LIODPDRV { + + public: + + DECLARE_CONSTRUCTOR( FAT_VOL ); + + VIRTUAL + ~FAT_VOL( + ); + + NONVIRTUAL + BOOLEAN + Initialize( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message DEFAULT NULL, + IN BOOLEAN ExclusiveWrite DEFAULT FALSE, + IN BOOLEAN FormatMedia DEFAULT FALSE, + IN MEDIA_TYPE MediaType DEFAULT Unknown + ); + + NONVIRTUAL + BOOLEAN + IsFileContiguous( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message DEFAULT NULL, + OUT PULONG NumBlocks DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + ContiguityReport( + IN PCWSTRING DirectoryPath, + IN PCDSTRING FilesToCheck, + IN ULONG NumberOfFiles, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + PVOL_LIODPDRV + QueryDupVolume( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message DEFAULT NULL, + IN BOOLEAN ExclusiveWrite DEFAULT FALSE, + IN BOOLEAN FormatMedia DEFAULT FALSE, + IN MEDIA_TYPE MediaType DEFAULT Unknown + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + REAL_FAT_SA _fatsa; + +}; + + +#else // _SETUP_LOADER_ is defined + + +class FAT_VOL : public VOL_LIODPDRV { + + public: + + DECLARE_CONSTRUCTOR( FAT_VOL ); + + VIRTUAL + ~FAT_VOL( + ); + + NONVIRTUAL + BOOLEAN + Initialize( + IN ULONG DeviceHandle + ); + + VIRTUAL + ARC_STATUS + MarkDirty( + ); + + VIRTUAL + ARC_STATUS + Flush( + IN BOOLEAN JustHandle + ); + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + REAL_FAT_SA _fatsa; + +}; + + + +#endif // _SETUP_LOADER_ + +#endif diff --git a/private/utils/ufat/inc/filedir.hxx b/private/utils/ufat/inc/filedir.hxx new file mode 100644 index 000000000..0c8d6097a --- /dev/null +++ b/private/utils/ufat/inc/filedir.hxx @@ -0,0 +1,213 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + filedir.hxx + +Abstract: + + This class is an implementation of FATDIR for all FAT directories + that are implemented as files. In other words all FAT directories + besides the root directory. + +Author: + + Norbert P. Kusters (norbertk) 4-Dec-90 + +--*/ + +#if !defined(FILEDIR_DEFN) + +#define FILEDIR_DEFN + +#include "cluster.hxx" +#include "drive.hxx" +#include "fatdir.hxx" +#include "mem.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( FILEDIR ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); + +class FILEDIR : public FATDIR { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR( FILEDIR ); + + VIRTUAL + UFAT_EXPORT + ~FILEDIR( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT StartingCluster + ); + + NONVIRTUAL + BOOLEAN + Read( + ); + + NONVIRTUAL + BOOLEAN + Write( + ); + + NONVIRTUAL + PVOID + GetDirEntry( + IN LONG EntryNumber + ); + + NONVIRTUAL + USHORT + QueryLength( + ) CONST; + + private: + + NONVIRTUAL + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + CLUSTER_CHAIN _cluster; + LONG _number_of_entries; + +}; + +INLINE +BOOLEAN +FILEDIR::Read( + ) +/*++ + +Routine Description: + + This routine reads the directory in from disk. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return _cluster.Read(); +} + + +INLINE +BOOLEAN +FILEDIR::Write( + ) +/*++ + +Routine Description: + + This routine writes the directory to disk. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return _cluster.Write(); +} + + +INLINE +PVOID +FILEDIR::GetDirEntry( + IN LONG EntryNumber + ) +/*++ + +Routine Description: + + This routine returns a pointer to the beginning of the requested + directory entry. The data may then be interpreted with a fat + directory entry object. The return value will be NULL if a request + for a directory entry is beyond the size of the directory. + +Arguments: + + EntryNumber - The desired entry number. The entries will be numbered + 0, 1, 2, ... , n-1. + +Return Value: + + A pointer to the beginning of a directory entry or NULL. + +--*/ +{ + return (EntryNumber < _number_of_entries) ? + (PCHAR) _cluster.GetBuf() + BytesPerDirent*EntryNumber : NULL; +} + + +INLINE +USHORT +FILEDIR::QueryLength( + ) CONST +/*++ + +Routine Description: + + This routine returns the number of clusters in the underlying cluster + chain. + +Arguments: + + None. + +Return Value: + + The number of clusters in the cluster chain. + +--*/ +{ + return _cluster.QueryLength(); +} + + +#endif // FILEDIR_DEFN diff --git a/private/utils/ufat/inc/mrcf.h b/private/utils/ufat/inc/mrcf.h new file mode 100644 index 000000000..0af302cdb --- /dev/null +++ b/private/utils/ufat/inc/mrcf.h @@ -0,0 +1,99 @@ +/*++ BUILD Version: 0001 // Increment this if a change has global effects + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Mrcf.h + +Abstract: + + This module defines all of the double space compression routines + +Author: + + Gary Kimura [GaryKi] 03-Jun-1993 + +Revision History: + +--*/ + +#ifndef _MRCF_ +#define _MRCF_ + +// +// To decompress/compress a block of data the user needs to +// provide a work space as an extra parameter to all the exported +// procedures. That way the routines will not need to use excessive +// stack space and will still be multithread safe +// + +// +// Variables for reading and writing bits +// + +typedef struct _MRCF_BIT_IO { + + USHORT abitsBB; // 16-bit buffer being read + LONG cbitsBB; // Number of bits left in abitsBB + + PUCHAR pbBB; // Pointer to byte stream being read + ULONG cbBB; // Number of bytes left in pbBB + ULONG cbBBInitial; // Initial size of pbBB + +} MRCF_BIT_IO; +typedef MRCF_BIT_IO *PMRCF_BIT_IO; + +// +// Decompression only needs the bit i/o structure +// + +typedef struct _MRCF_DECOMPRESS { + + MRCF_BIT_IO BitIo; + +} MRCF_DECOMPRESS; +typedef MRCF_DECOMPRESS *PMRCF_DECOMPRESS; + +// +// Standard compression uses a few more field to contain +// the lookup table +// + +#define cMAXSLOTS (8) // The maximum number of slots used in the algorithm + +#define ltUNUSED (0xE000) // Value of unused ltX table entry +#define mruUNUSED (0xFF) // Value of unused MRU table entry +#define bRARE (0xD5) // Rarely occuring character value + +typedef struct _MRCF_STANDARD_COMPRESS { + + MRCF_BIT_IO BitIo; + + ULONG ltX[256][cMAXSLOTS]; // Source text pointer look-up table + UCHAR abChar[256][cMAXSLOTS]; // Character look-up table + UCHAR abMRUX[256]; // Most Recently Used ltX/abChar entry + +} MRCF_STANDARD_COMPRESS; +typedef MRCF_STANDARD_COMPRESS *PMRCF_STANDARD_COMPRESS; + +ULONG +MrcfDecompress ( + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PMRCF_DECOMPRESS WorkSpace + ); + +ULONG +MrcfStandardCompress ( + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PMRCF_STANDARD_COMPRESS WorkSpace + ); + +#endif // _MRCF_ + diff --git a/private/utils/ufat/inc/reloclus.hxx b/private/utils/ufat/inc/reloclus.hxx new file mode 100644 index 000000000..c59da72c8 --- /dev/null +++ b/private/utils/ufat/inc/reloclus.hxx @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + reloclus.hxx + +Abstract: + + This modules contains the declaration of the RELOCATION_CLUSTER + class. A relocation cluster is a cluster that will be relocated + somewhere else. + +Author: + + Ramon J. San Andres (ramonsa) 05-Nov-1991 + +--*/ + +#if !defined( _RELOCATION_CLUSTER_ ) + +#define _RELOCATION_CLUSTER_ + +DECLARE_CLASS( RELOCATION_CLUSTER); + +class RELOCATION_CLUSTER : public OBJECT { + + public: + + DECLARE_CONSTRUCTOR( RELOCATION_CLUSTER ); + DECLARE_CAST_MEMBER_FUNCTION( RELOCATION_CLUSTER ); + + VIRTUAL + ~RELOCATION_CLUSTER( + ); + + NONVIRTUAL + BOOLEAN + Initialize ( + IN USHORT ClusterNumber + ); + + VIRTUAL + LONG + Compare ( + IN PCOBJECT Object + ) CONST; + + NONVIRTUAL + USHORT + QueryClusterNumber ( + ) CONST; + + + private: + + USHORT _Cluster; // Cluster + +}; + + +INLINE +USHORT +RELOCATION_CLUSTER::QueryClusterNumber ( + ) CONST +/*++ + +Routine Description: + + Obtains the cluster number of this relocation cluster + +Arguments: + + None. + +Return Value: + + USHORT - cluster number + +--*/ +{ + return _Cluster; +} + + +#endif // _RELOCATION_CLUSTER_ diff --git a/private/utils/ufat/inc/rfatsa.hxx b/private/utils/ufat/inc/rfatsa.hxx new file mode 100644 index 000000000..192102af4 --- /dev/null +++ b/private/utils/ufat/inc/rfatsa.hxx @@ -0,0 +1,986 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rfatsa.hxx + +Abstract: + +Author: + + Mark Shavlik (marks) 27-Mar-90 + Norbert Kusters (norbertk) 15-Jan-91 + Matthew Bradburn (mattbr) 01-Oct-93 + +--*/ + +#ifndef REAL_FAT_SA_DEFN +#define REAL_FAT_SA_DEFN + +#include "hmem.hxx" +#include "message.hxx" +#include "fatsa.hxx" +#include "bpb.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +// +// Forward references +// + +DECLARE_CLASS( ARRAY ); +DECLARE_CLASS( BITVECTOR ); +DECLARE_CLASS( EA_HEADER ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( FAT_DIRENT ); +DECLARE_CLASS( FATDIR ); +DECLARE_CLASS( GENERIC_STRING ); +DECLARE_CLASS( INTSTACK ); +DECLARE_CLASS( NUMBER_SET ); +DECLARE_CLASS( LOG_IO_DP_DRIVE ); +DECLARE_CLASS( MESSAGE ); +DECLARE_CLASS( ROOTDIR ); +DECLARE_CLASS( SORTED_LIST ); +DECLARE_CLASS( TIMEINFO ); +DECLARE_CLASS( WSTRING ); +DEFINE_POINTER_TYPES( PFATDIR ); + +class REAL_FAT_SA : public FAT_SA { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR(REAL_FAT_SA); + + VIRTUAL + UFAT_EXPORT + ~REAL_FAT_SA( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN OUT PLOG_IO_DP_DRIVE Drive, + IN OUT PMESSAGE Message, + IN BOOLEAN Formatted DEFAULT TRUE + ); + + NONVIRTUAL + BOOLEAN + Create( + IN PCNUMBER_SET BadSectors, + IN OUT PMESSAGE Message, + IN PCWSTRING Label DEFAULT NULL, + IN ULONG ClusterSize DEFAULT 0, + IN ULONG VirtualSize DEFAULT 0 + ); + + NONVIRTUAL + BOOLEAN + RecoverFile( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Read( + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + Write( + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + VOID + QueryGeometry( + OUT PUSHORT SectorSize, + OUT PUSHORT SectorsPerTrack, + OUT PUSHORT Heads, + OUT PULONG HiddenSectors + ); + + NONVIRTUAL + USHORT + QuerySectorsPerCluster( + ) CONST; + + NONVIRTUAL + USHORT + QuerySectorsPerFat( + ) CONST; + + NONVIRTUAL + USHORT + QueryReservedSectors( + ) CONST; + + NONVIRTUAL + USHORT + QueryFats( + ) CONST; + + NONVIRTUAL + USHORT + QueryRootEntries( + ) CONST; + + NONVIRTUAL + PARTITION_SYSTEM_ID + QuerySystemId( + ) CONST; + + NONVIRTUAL + LBN + QueryStartDataLbn( + ) CONST; + + NONVIRTUAL + USHORT + QueryClusterCount( + ) CONST; + + NONVIRTUAL + UFAT_EXPORT + SECTORCOUNT + QueryFreeSectors( + ) CONST; + + NONVIRTUAL + FATTYPE + QueryFatType( + ) CONST; + + NONVIRTUAL + BYTE + QueryVolumeFlags( + ) CONST; + + NONVIRTUAL + VOID + SetVolumeFlags( + BYTE Flags, + BOOLEAN ResetFlags + ); + + NONVIRTUAL + BOOLEAN + RecoverChain( + IN OUT PUSHORT StartingCluster, + OUT PBOOLEAN ChangesMade, + IN USHORT EndingCluster DEFAULT 0, + IN BOOLEAN Replace DEFAULT FALSE + ); + + STATIC + USHORT + ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ); + + NONVIRTUAL + BOOLEAN + IsCompressed( + ) CONST; + + NONVIRTUAL + BOOLEAN + ReadSectorZero( + ); + + NONVIRTUAL + PBIOS_PARAMETER_BLOCK + GetBpb( + ); + + private: + + HMEM _mem; // memory for SECRUN + + // _fat inherited from FAT_SA + // _fattype inherited from FAT_SA + // _dir inherited from FAT_SA + + LBN _StartDataLbn; // LBN of files, or data area + USHORT _ClusterCount; // number of clusters in Super Area + PARTITION_SYSTEM_ID _sysid; // system id + ULONG _sec_per_boot; // sectors for boot code. + EXTENDED_BIOS_PARAMETER_BLOCK + _sector_zero; + PUCHAR _sector_sig; // sector signature + + NONVIRTUAL + VOID + Construct( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + NONVIRTUAL + BOOLEAN + SetBpb( + IN ULONG ClusterSize + ); + + NONVIRTUAL + BOOLEAN + SetBpb( + ); + + NONVIRTUAL + BOOLEAN + DupFats( + ); + + NONVIRTUAL + LBN + ComputeStartDataLbn( + ) CONST; + + NONVIRTUAL + USHORT + ComputeRootEntries( + ) CONST; + + NONVIRTUAL + BOOLEAN + ValidateDirent( + IN OUT PFAT_DIRENT Dirent, + IN PCWSTRING FilePath, + IN FIX_LEVEL FixLevel, + IN BOOLEAN RecoverAlloc, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage, + IN OUT PBITVECTOR FatBitMap, + OUT PBOOLEAN CrossLinkDetected, + OUT PUSHORT CrossLinkPreviousCluster + ); + + NONVIRTUAL + BOOLEAN + CopyClusters( + IN USHORT SourceChain, + OUT PUSHORT DestChain, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message + ); + + NONVIRTUAL + BOOLEAN + InitRelocationList( + IN OUT PINTSTACK RelocationStack, + IN OUT PUSHORT RelocatedChain, + IN OUT PSORTED_LIST ClustersToRelocate, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + BOOLEAN + RelocateFirstCluster( + IN OUT PFAT_DIRENT Dirent + ); + + NONVIRTUAL + USHORT + RelocateOneCluster( + IN USHORT Cluster, + IN USHORT Previous + ); + + NONVIRTUAL + BOOLEAN + DoDirectoryCensusAndRelocation( + IN OUT PFATDIR Directory, + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + BOOLEAN + DoVolumeCensusAndRelocation( + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ); + + NONVIRTUAL + ULONG + SecPerBoot( + ); + + NONVIRTUAL + VOLID + QueryVolId( + ) CONST; + + NONVIRTUAL + VOLID + SetVolId( + IN VOLID VolId + ); + + NONVIRTUAL + UCHAR + QueryMediaByte( + ) CONST; + + VIRTUAL + VOID + SetMediaByte( + UCHAR MediaByte + ); + + NONVIRTUAL + BOOLEAN + VerifyBootSector( + ); + + NONVIRTUAL + SECTORCOUNT + QueryVirtualSectors( + ) CONST; + + NONVIRTUAL + BOOLEAN + CreateBootSector( + IN ULONG ClusterSize + ); + + BOOLEAN + REAL_FAT_SA::SetBootCode( + ); + + NONVIRTUAL + BOOLEAN + SetPhysicalDriveType( + IN PHYSTYPE PhysType + ); + + NONVIRTUAL + BOOLEAN + SetOemData( + ); + + NONVIRTUAL + BOOLEAN + SetSignature( + ); + + NONVIRTUAL + BOOLEAN + SetBootSignature( + IN UCHAR Signature DEFAULT sigBOOTSTRAP + ); + + BOOLEAN + DosSaInit( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN SECTORCOUNT NumberOfSectors, + IN OUT PMESSAGE Message + ); + + BOOLEAN + DosSaSetBpb( + ); + + BOOLEAN + RecoverOrphans( + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ); + + NONVIRTUAL + ULONG + QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors DEFAULT NULL + ); + + NONVIRTUAL + BOOLEAN + IsClusterCompressed( + IN ULONG Cluster + ) CONST; + + NONVIRTUAL + VOID + SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN fCompressed + ); + + NONVIRTUAL + UCHAR + QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ); + + NONVIRTUAL + BOOLEAN + VerifyFatExtensions( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN PBOOLEAN pfNeedMsg + ); + + NONVIRTUAL + BOOLEAN + CheckSectorHeapAllocation( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN PBOOLEAN pfNeedMsg + ); + + NONVIRTUAL + BOOLEAN + FreeClusterData( + ULONG Cluster + ); + + NONVIRTUAL + BOOLEAN + AllocateClusterData( + ULONG Cluster, + UCHAR NumSectors, + BOOLEAN bCompressed, + UCHAR PlainSize + ); + + +}; + +INLINE +USHORT +REAL_FAT_SA::QuerySectorsPerCluster( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors per cluster for + the volume. + +Arguments: + + None. + +Return Value: + + The number of sectors per cluster for the volume. + +--*/ +{ + return _sector_zero.Bpb.SectorsPerCluster ? + _sector_zero.Bpb.SectorsPerCluster : 256; +} + + +INLINE +USHORT +REAL_FAT_SA::QuerySectorsPerFat( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors per FAT for the volume. + +Arguments: + + None. + +Return Value: + + The number of sectors per FAT for the volume. + +--*/ +{ + return _sector_zero.Bpb.SectorsPerFat; +} + + +INLINE +USHORT +REAL_FAT_SA::QueryReservedSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the volume's number of Reserved Sectors, + i.e. the number of sectors before the first FAT. + +Arguments: + + None. + +Return Value: + + The number of Reserved Sectors. + +--*/ +{ + return _sector_zero.Bpb.ReservedSectors; +} + +INLINE +USHORT +REAL_FAT_SA::QueryFats( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of FATs on the volume. + +Arguments: + + None. + +Return Value: + + The number of FATs on the volume. + +--*/ +{ + return _sector_zero.Bpb.Fats; +} + +INLINE +USHORT +REAL_FAT_SA::QueryRootEntries( + ) CONST +/*++ + +Routine Description: + + This routine returns the number of entries in the root + directory. + +Arguments: + + None. + +Return Value: + + The number of root directory entries. + +--*/ +{ + return _sector_zero.Bpb.RootEntries; +} + + +INLINE +PARTITION_SYSTEM_ID +REAL_FAT_SA::QuerySystemId( + ) CONST +/*++ + +Routine Description: + + This routine computes the system ID for the volume. + +Arguments: + + None. + +Return Value: + + The system ID for the volume. + +--*/ +{ + return _sysid; +} + + +INLINE +LBN +REAL_FAT_SA::QueryStartDataLbn( + ) CONST +/*++ + +Routine Description: + + This routine computes the LBN of the first logical cluster of the + volume. + +Arguments: + + None. + +Return Value: + + The LBN of the first logical cluster of the volume. + +--*/ +{ + return _StartDataLbn; +} + + +INLINE +USHORT +REAL_FAT_SA::QueryClusterCount( + ) CONST +/*++ + +Routine Description: + + This routine computes the total number of clusters for the volume. + That is to say that the largest addressable cluster on the disk + is cluster number 'QueryClusterCount() - 1'. Note that the + smallest addressable cluster on the disk is 2. + +Arguments: + + None. + +Return Value: + + The total number of clusters for the volume. + +--*/ +{ + return _ClusterCount; +} + +INLINE BOOLEAN +REAL_FAT_SA::IsCompressed( + ) CONST +/*++ + +Routine Description: + + This routine tells whether this volume is doublespaced or not. + Since the class is REAL_FAT_SA, we know it's not. + +Arguments: + +Return Value: + + TRUE - Compressed. + FALSE - Not compressed. + +--*/ +{ + return FALSE; +} + +INLINE BOOLEAN +REAL_FAT_SA::ReadSectorZero( + ) +/*++ + +Routine Description: + + This routine used to be DOS_SUPERAREA::Read(). + +Arguments: + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + BOOLEAN b; + PEXTENDED_BIOS_PARAMETER_BLOCK Pbios; + + b = SECRUN::Read(); + if (!b) + return FALSE; + + Pbios = (PEXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf(); + UnpackExtendedBios(&_sector_zero, Pbios); + + return TRUE; +} + +INLINE +PBIOS_PARAMETER_BLOCK +REAL_FAT_SA::GetBpb( + ) +{ + return &(_sector_zero.Bpb); +} + + + +INLINE +UCHAR +REAL_FAT_SA::QueryMediaByte( + ) CONST +/*++ + +Routine Description: + + This routine fetches the media byte from the super area's data. + +Arguments: + + None. + +Return Value: + + The media byte residing in the super area. + +--*/ +{ + return _sector_zero.Bpb.Media; +} + +INLINE +VOID +REAL_FAT_SA::SetMediaByte( + UCHAR MediaByte + ) +/*++ + +Routine Description: + + This routine sets the media byte in the super area's data. + +Arguments: + + MediaByte -- Supplies the new media byte. + +Return Value: + + None. + +--*/ +{ + _sector_zero.Bpb.Media = MediaByte; +} + +INLINE +SECTORCOUNT +REAL_FAT_SA::QueryVirtualSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of sectors on the volume according + to the file system. + +Arguments: + + None. + +Return Value: + + The number of sectors on the volume according to the file system. + +--*/ +{ + return _sector_zero.Bpb.Sectors ? _sector_zero.Bpb.Sectors : + _sector_zero.Bpb.LargeSectors; +} + +INLINE +VOLID +REAL_FAT_SA::QueryVolId( + ) CONST +/*++ + +Routine Description: + + This routine fetches the volume ID from the super area's data. + This routine will return 0 if volume serial numbers are not + supported by the partition. + +Arguments: + + None. + +Return Value: + + The volume ID residing in the super area. + +--*/ +{ + return (_sector_zero.Signature == 0x28 || _sector_zero.Signature == 0x29) ? + _sector_zero.SerialNumber : 0; +} + +INLINE +VOLID +REAL_FAT_SA::SetVolId( + IN VOLID VolId + ) +/*++ + +Routine Description: + + This routine puts the volume ID into the super area's data. + +Arguments: + + VolId - The new volume ID. + +Return Value: + + The volume ID that was put. + +--*/ +{ + return _sector_zero.SerialNumber = VolId; +} + +INLINE +BOOLEAN +REAL_FAT_SA::SetBootSignature( + IN UCHAR Signature + ) +/*++ + +Routine Description: + + This routine sets the boot signature in the super area. + +Arguments: + + Signature - Supplies the character to set the signature to. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + _sector_zero.Signature = Signature; + return TRUE; +} + +INLINE +VOID +REAL_FAT_SA::QueryGeometry( + OUT PUSHORT SectorSize, + OUT PUSHORT SectorsPerTrack, + OUT PUSHORT Heads, + OUT PULONG HiddenSectors + ) +/*++ + +Routine Description: + + This method returns the geometry information stored in + the Bios Parameter Block. + +Arguments: + + SectorSize -- Receives the recorded sector size. + SectorsPerTrack -- Receives the recorded sectors per track. + Heads -- Receives the recorded number of heads. + HiddenSectors -- Receives the recorded number of hidden sectors. + +Return Value: + + None. + +--*/ +{ + *SectorSize = _sector_zero.Bpb.BytesPerSector; + *SectorsPerTrack = _sector_zero.Bpb.SectorsPerTrack; + *Heads = _sector_zero.Bpb.Heads; + *HiddenSectors = _sector_zero.Bpb.HiddenSectors; +} + +INLINE +BYTE +REAL_FAT_SA::QueryVolumeFlags( + ) CONST +/*++ + +Routine Description: + + This routine returns the volume flags byte from the bpb. + +Arguments: + + None. + +Return Value: + + The flags. + +--*/ +{ + return _sector_zero.CurrentHead; +} + +INLINE +VOID +REAL_FAT_SA::SetVolumeFlags( + BYTE Flags, + BOOLEAN ResetFlags + ) +/*++ + +Routine Description: + + This routine sets the volume flags in the bpb. + +Arguments: + + Flags -- flags to set or clear + ResetFlags -- if true, Flags are cleared instead of set + +Return Value: + + None. + +--*/ +{ + if (ResetFlags) { + _sector_zero.CurrentHead &= ~Flags; + } else { + _sector_zero.CurrentHead |= Flags; + } +} + +INLINE +BOOLEAN +AllocateClusterData( + ULONG Cluster, + UCHAR NumSectors, + BOOLEAN bCompressed, + UCHAR PlainSize + ) +{ + DebugAbort("Didn't expect REAL_FAT_SA::AllocateClusterData() to be called"); + return FALSE; +} + +#endif // REAL_FAT_SA_DEFN diff --git a/private/utils/ufat/inc/rootdir.hxx b/private/utils/ufat/inc/rootdir.hxx new file mode 100644 index 000000000..8741500d5 --- /dev/null +++ b/private/utils/ufat/inc/rootdir.hxx @@ -0,0 +1,173 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rootdir.hxx + +Abstract: + + This class is an implementation of FATDIR for the FAT root directory. + +Author: + + Norbert P. Kusters (norbertk) 4-Dec-90 + +--*/ + +#if !defined(ROOTDIR_DEFN) + +#define ROOTDIR_DEFN + +#include "drive.hxx" +#include "fatdir.hxx" +#include "mem.hxx" +#include "secrun.hxx" + +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + +DECLARE_CLASS( ROOTDIR ); + + +class ROOTDIR : public FATDIR { + + public: + + UFAT_EXPORT + DECLARE_CONSTRUCTOR( ROOTDIR ); + + UFAT_EXPORT + VIRTUAL + ~ROOTDIR( + ); + + NONVIRTUAL + UFAT_EXPORT + BOOLEAN + Initialize( + IN PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartingSector, + IN LONG NumberOfEntries + ); + + NONVIRTUAL + BOOLEAN + Read( + ); + + NONVIRTUAL + BOOLEAN + Write( + ); + + NONVIRTUAL + PVOID + GetDirEntry( + IN LONG EntryNumber + ); + + private: + + VOID + Construct ( + ); + + NONVIRTUAL + VOID + Destroy( + ); + + SECRUN _secrun; + LONG _number_of_entries; + +}; + +INLINE +BOOLEAN +ROOTDIR::Read( + ) +/*++ + +Routine Description: + + This routine reads the directory in from the disk. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return _secrun.Read(); +} + + +INLINE +BOOLEAN +ROOTDIR::Write( + ) +/*++ + +Routine Description: + + This routine writes the drirectory to the disk. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return _secrun.Write(); +} + + +INLINE +PVOID +ROOTDIR::GetDirEntry( + IN LONG EntryNumber + ) +/*++ + +Routine Description: + + This routine returns a pointer to the beginning of the requested + directory entry. The data may then be interpreted with a fat + directory entry object. The return value will be NULL if a request + for a directory entry is beyond the size of the directory. + +Arguments: + + EntryNumber - The desired entry number. The entries will be numbered + 0, 1, 2, ... , n-1. + +Return Value: + + A pointer to the beginning of a directory entry or NULL. + +--*/ +{ + return (EntryNumber < _number_of_entries) ? + (PCHAR) _secrun.GetBuf() + BytesPerDirent*EntryNumber : NULL; +} + + +#endif // ROOTDIR_DEFN diff --git a/private/utils/ufat/inc/ufat.hxx b/private/utils/ufat/inc/ufat.hxx new file mode 100644 index 000000000..d3fa4219f --- /dev/null +++ b/private/utils/ufat/inc/ufat.hxx @@ -0,0 +1,36 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ufat.hxx + +Author: + + Rafael Lisitsa (RafaelL) 23-Mar-1995 + + +--*/ + + +#if !defined ( _UFAT_INCLUDED_ ) + +#define _UFAT_INCLUDED_ + + +// Set up the UFAT_EXPORT macro for exporting from UFAT (if the +// source file is a member of UFAT) or importing from UFAT (if +// the source file is a client of UFAT). +// +#if defined ( _AUTOCHECK_ ) +#define UFAT_EXPORT +#elif defined ( _UFAT_MEMBER_ ) +#define UFAT_EXPORT __declspec(dllexport) +#else +#define UFAT_EXPORT __declspec(dllimport) +#endif + + +#endif // _UFAT_INCLUDED_ + diff --git a/private/utils/ufat/src/cluster.cxx b/private/utils/ufat/src/cluster.cxx new file mode 100644 index 000000000..3b878ed46 --- /dev/null +++ b/private/utils/ufat/src/cluster.cxx @@ -0,0 +1,523 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + + +#include "cmem.hxx" + +extern "C" { +#ifdef DBLSPACE_ENABLED +#include "mrcf.h" +#endif // DBLSPACE_ENABLED +#include "ntrtl.h" +} + +DEFINE_EXPORTED_CONSTRUCTOR( CLUSTER_CHAIN, OBJECT, UFAT_EXPORT ); + +VOID +CLUSTER_CHAIN::Construct ( + ) +/*++ + +Routine Description: + + Constructor for CLUSTER_CHAIN which initializes private data to + default values. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _secruns = NULL; + _num_secruns = 0; + _length_of_chain = 0; + _is_compressed = FALSE; + _buf = NULL; + _drive = NULL; + _fat_sa = NULL; + _secrun = NULL; +} + +UFAT_EXPORT +CLUSTER_CHAIN::~CLUSTER_CHAIN( + ) +/*++ + +Routine Description: + + Destructor for CLUSTER_CHAIN. Frees memory and returns references. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +CLUSTER_CHAIN::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT ClusterNumber, + IN USHORT LengthOfChain + ) +/*++ + +Routine Description: + + Prepares the CLUSTER_CHAIN object for reads and writes to disk. + The length of the cluster chain may be specified by the + LengthOfChain argument. Setting this parameter to 0 will cause + the length of the chain to be until the end of file. + +Arguments: + + Mem - Supplies memory for the cluster object. + Drive - Supplies the drive to which reads and writes will + take place. + FatSuperArea - Supplies the FAT super area which contains information + about the current FAT implementation. + Fat - Supplies the file allocation table for this drive. + ClusterNumber - Supplies the cluster number to map. + LengthOfChain - Supplies the number of clusters in the chain. + This value defaults to 0 which indicates that all + clusters until end of file will be addressed. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + CONT_MEM cmem; + SECTORCOUNT sec_per_clus; + USHORT clus; + LONG size; + PVOID buf; + ULONG i, j; + LBN lbn; + + Destroy(); + + if (!Mem || + !Drive || + !FatSuperArea || + !Fat || + !Fat->IsInRange(ClusterNumber)) { + Destroy(); + return FALSE; + } + + if (LengthOfChain) { + _length_of_chain = LengthOfChain; + } else { + _length_of_chain = Fat->QueryLengthOfChain(ClusterNumber); + } + + if (!_length_of_chain) { + Destroy(); + return FALSE; + } + + sec_per_clus = FatSuperArea->QuerySectorsPerCluster(); + size = sec_per_clus*Drive->QuerySectorSize()*_length_of_chain; + + _is_compressed = FatSuperArea->IsCompressed(); + _fat_sa = FatSuperArea; + +#ifdef DBLSPACE_ENABLED + if (_is_compressed) { + + _fat = Fat; + _drive = Drive; + _starting_cluster = ClusterNumber; + + if (!(_secrun = NEW SECRUN)) { + Destroy(); + return FALSE; + } + + // This buf will hold the cluster chain's uncompressed data. + + if (!(_buf = (PUCHAR)Mem->Acquire(size, Drive->QueryAlignmentMask()))) { + Destroy(); + return FALSE; + } + + if (!_hmem.Initialize()) { + Destroy(); + return FALSE; + } + + return TRUE; + } +#endif // DBLSPACE_ENABLED + + if (!(_secruns = (PSECRUN*) MALLOC(_length_of_chain*sizeof(PSECRUN)))) { + Destroy(); + return FALSE; + } + + if (!(buf = Mem->Acquire(size, Drive->QueryAlignmentMask())) || + !cmem.Initialize(buf, size)) { + Destroy(); + return FALSE; + } + + clus = ClusterNumber; + i = 0; + for (;;) { + lbn = _fat_sa->QuerySectorFromCluster(clus); + + for (j = 1; !Fat->IsEndOfChain(clus) && + (USHORT)(clus + 1) == Fat->QueryEntry(clus) && + i + j < _length_of_chain; j++) { + clus++; + } + i += j; + + if (!(_secruns[_num_secruns] = NEW SECRUN)) { + Destroy(); + return FALSE; + } + + if (!_secruns[_num_secruns]->Initialize(&cmem, Drive, lbn, + j*sec_per_clus)) { + Destroy(); + return FALSE; + } + + _num_secruns++; + + if (i == _length_of_chain) { + break; + } + + clus = Fat->QueryEntry(clus); + if (!Fat->IsInRange(clus)) { + Destroy(); + return FALSE; + } + } + + return TRUE; +} + + +UFAT_EXPORT +BOOLEAN +CLUSTER_CHAIN::Read( + ) +/*++ + +Routine Description: + + This routine reads the cluster chain into memory. This is done + by making repetitive use of SECRUN's Read routine. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + ULONG i; + BOOLEAN b = TRUE; + USHORT clus; + LBN lbn; + UCHAR nsec; +#ifdef DBLSPACE_ENABLED + MRCF_DECOMPRESS + wkspc; +#endif // DBLSPACE_ENABLED + ULONG u; + ULONG cluster_size; + ULONG sector_size; + + if (!_is_compressed) { + + if (!_secruns) { + return FALSE; + } + + for (i = 0; i < _num_secruns; i++) { + + b = (BOOLEAN)(_secruns[i]->Read() && b); + } + + return b; + } + +#ifdef DBLSPACE_ENABLED + // + // The volume is compressed. + // + + sector_size = _drive->QuerySectorSize(); + cluster_size = sector_size * _fat_sa->QuerySectorsPerCluster(); + + clus = _starting_cluster; + + i = 0; + for (;;) { + + lbn = _fat_sa->QuerySectorFromCluster(clus, &nsec); + + if (!_secrun->Initialize(&_hmem, _drive, lbn, nsec)) { + return FALSE; + } + + if (!_secrun->Read()) { + return FALSE; + } + + if (_fat_sa->IsClusterCompressed(clus)) { + + RtlZeroMemory(&_buf[i * sector_size], cluster_size); + + u = MrcfDecompress(&_buf[i * sector_size], + _fat_sa->QuerySectorsRequiredForPlainData(clus) * sector_size, + (PUCHAR)_secrun->GetBuf(), + nsec * sector_size, &wkspc); + + if (0 == u) { + // error: can't decompress data + return TRUE; + } + + } else { + + // This cluster isn't compressed; just copy. + + memcpy(&_buf[i * cluster_size], _secrun->GetBuf(), cluster_size); + } + + if (++i == _length_of_chain) { + return TRUE; + } + + clus = _fat->QueryEntry(clus); + if (!_fat->IsInRange(clus)) { + return FALSE; + } + } + + //NOTREACHED +#endif // DBLSPACE_ENABLED + return FALSE; +} + + +UFAT_EXPORT +BOOLEAN +CLUSTER_CHAIN::Write( + ) +/*++ + +Routine Description: + + This routine writes the cluster chain to disk. This is done + by making repetitive use of SECRUN's Write routine. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + ULONG i; + BOOLEAN b = TRUE; + UCHAR nsec, new_nsec; + USHORT clus; + LBN lbn; +#ifdef DBLSPACE_ENABLED + MRCF_STANDARD_COMPRESS + wkspc; +#endif // DBLSPACE_ENABLED + ULONG cluster_size; + ULONG sector_size; + ULONG u; + HMEM work_buf; + + if (!_is_compressed) { + if (!_secruns) { + return FALSE; + } + + for (i = 0; i < _num_secruns; i++) { + + b = (BOOLEAN)(_secruns[i]->Write() && b); + } + + return b; + } + +#ifdef DBLSPACE_ENABLED + // + // The volume is compressed. + // + + sector_size = _drive->QuerySectorSize(); + cluster_size = _fat_sa->QuerySectorsPerCluster() * sector_size; + + if (!work_buf.Initialize() || + !work_buf.Acquire(cluster_size, _drive->QueryAlignmentMask())) { + return FALSE; + } + + clus = _starting_cluster; + i = 0; + for (;;) { + lbn = _fat_sa->QuerySectorFromCluster(clus, &nsec); + + u = MrcfStandardCompress((PUCHAR)work_buf.GetBuf(), cluster_size, + &_buf[i * cluster_size], cluster_size, &wkspc); + if (0 == u) { + // the data could not be compressed + + if (nsec < _fat_sa->QuerySectorsPerCluster()) { + + // + // previously the data had been compressed; need to allocate + // more disk space. + // + + nsec = _fat_sa->QuerySectorsPerCluster(); + _fat_sa->FreeClusterData(clus); + if (!_fat_sa->AllocateClusterData(clus, nsec, FALSE, + _fat_sa->QuerySectorsPerCluster())) { + // error: no space + return FALSE; + } + + lbn = _fat_sa->QuerySectorFromCluster(clus); + } + DbgAssert(nsec == _fat_sa->QuerySectorsPerCluster()); + + _fat_sa->SetClusterCompressed(clus, FALSE); + + } else { + new_nsec = (u + sector_size - 1)/sector_size; + + if (new_nsec != nsec) { + + // + // The data has been changed, and it won't compress into + // the same size as it used to. + // + + _fat_sa->FreeClusterData(clus); + if (!_fat_sa->AllocateClusterData(clus, new_nsec, TRUE, + _fat_sa->QuerySectorsPerCluster())) { + + // error: not enough free space + return FALSE; + } + lbn = _fat_sa->QuerySectorFromCluster(clus, &nsec); + } else { + + // + // The cluster may not have been compressed before, + // but we still need the same amount of space even though + // it's now compressed. + // + + _fat_sa->SetClusterCompressed(clus, TRUE); + } + } + + _hmem.Initialize(); + if (!_secrun->Initialize(&_hmem, _drive, lbn, nsec)) { + return FALSE; + } + + memcpy(_secrun->GetBuf(), work_buf.GetBuf(), + nsec * sector_size); + + b = (BOOLEAN)(_secrun->Write() && b); + + if (++i == _length_of_chain) { + return b; + } + + clus = _fat->QueryEntry(clus); + if (!_fat->IsInRange(clus)) { + return FALSE; + } + } + + //NOTREACHED +#endif // DBLSPACE_ENABLED + return FALSE; +} + + +VOID +CLUSTER_CHAIN::Destroy( + ) +/*++ + +Routine Description: + + This routine cleans up the objects internal components. It does not + need to be called before Init as Init does this automatically. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG i; + +#ifdef DBLSPACE_ENABLED + if (_is_compressed) { + DELETE(_secrun); + _num_secruns = 0; + _length_of_chain = 0; + _buf = NULL; + _drive = NULL; + return; + } +#endif // DBLSPACE_ENABLED + + for (i = 0; i < _num_secruns; i++) { + DELETE(_secruns[i]); + } + + DELETE(_secruns); + _num_secruns = 0; + _length_of_chain = 0; +} diff --git a/private/utils/ufat/src/cvfexts.cxx b/private/utils/ufat/src/cvfexts.cxx new file mode 100644 index 000000000..73286f400 --- /dev/null +++ b/private/utils/ufat/src/cvfexts.cxx @@ -0,0 +1,129 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + cvfexts.hxx + +Abstract: + + A class to manage the CVF_FAT_EXTENSIONS (otherwise known as + the MDFAT) part of the doublespace volume. + +Author: + + Matthew Bradburn (mattbr) 27-Sep-93 + +--*/ + +#include <pch.cxx> + +extern "C" { +#include "ntdef.h" +#include "nturtl.h" +} + +DEFINE_CONSTRUCTOR( CVF_FAT_EXTENS, SECRUN ); + +BOOLEAN +CVF_FAT_EXTENS::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartSector, + IN ULONG NumberOfEntries, + IN ULONG FirstEntry + ) +/*++ + +Routine Description: + + This routine initializes a CVF_FAT_EXTENS object. + +Arguments: + + Mem - supplies the memory for the run of sectors. + Drive - supplies the drive to read and write from. + StartSector - supplies the start of the mdfat + NumberOfEntries - supplies the number of entries in the mdfat + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + ULONG sector_size; + SECTORCOUNT nsec; + + Destroy(); + + DbgAssert(Mem); + DbgAssert(Drive); + + if (0 == (sector_size = Drive->QuerySectorSize())) { + Destroy(); + return FALSE; + } + + nsec = (NumberOfEntries*sizeof(ULONG) + sector_size)/sector_size; + + if (!SECRUN::Initialize(Mem, Drive, StartSector, nsec)) { + Destroy(); + return FALSE; + } + + _mdfat = (PULONG)GetBuf() + FirstEntry; // - FirstDiskCluster*sizeof(ULONG); + + _num_entries = NumberOfEntries; + _first_entry = FirstEntry; + + return TRUE; +} + +CVF_FAT_EXTENS::~CVF_FAT_EXTENS( + ) +{ + Destroy(); +} + +VOID +CVF_FAT_EXTENS::Destroy( + ) +{ + _mdfat = NULL; +} + +VOID +CVF_FAT_EXTENS::Construct( + ) +{ + _mdfat = NULL; +} + +BOOLEAN +CVF_FAT_EXTENS::Create( + ) +/*++ + +Routine Description: + + This routine will make the CVF_FAT_EXTENSIONS to be valid + but empty. + +Arguments: + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + for (ULONG i = 0; i < _num_entries; ++i) { + _mdfat[i] = 0; + SetClusterInUse(i, FALSE); + } + return TRUE; +} diff --git a/private/utils/ufat/src/dblentry.cxx b/private/utils/ufat/src/dblentry.cxx new file mode 100644 index 000000000..95802c0d1 --- /dev/null +++ b/private/utils/ufat/src/dblentry.cxx @@ -0,0 +1,1177 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + entry.cxx + +Abstract: + + This module contains the entry points for UFAT.DLL: + + FatDbCreate + FatDbDelete + FatDbFormat + FatDbChkdsk + +Author: + + Bill McJohn (billmc) 31-05-91 + +Environment: + + ULIB, User Mode + +--*/ + +#include <pch.cxx> + +#include "hmem.hxx" +#include "message.hxx" +#include "wstring.hxx" +#include "bigint.hxx" +#include "ifssys.hxx" +#include "drive.hxx" +#include "ifsentry.hxx" + +#include "rtmsg.h" +#include "ntstatus.h" + +extern "C" { +#include <stdio.h> +} + + +// this is used to cancel out the definition of DeleteFile +// as DeleteFileW, so that the DeleteFile member of the +// FILE_DISPOSITION_INFORMATION structure can be visible. +// +#undef DeleteFile + +BOOLEAN +QueryHostFileName( + IN PCWSTRING DriveName, + OUT PWSTRING HostFileName + ) +/*++ + +Routine Description: + + This function determines the host file name for a mounted + Double Space volume. + +Arguments: + + DriveName -- Supplies the volume's NT Drive name + HostFileName -- Receives the fully qualified NT path to + the Compressed Volume File. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + DSTRING DblspaceString; + CHNUM position; + + // By convention, the device name for a double space volume + // is "<host drive name>.DBLSPACE.nnn". Thus, this method + // traces down the symbolic links to the device name, and + // then replaces the dot before DBLSPACE with a backslash. + // + if( !DblspaceString.Initialize( "DBLSPACE" ) || + !IFS_SYSTEM::QueryCanonicalNtDriveName( DriveName, HostFileName ) ) { + + return FALSE; + } + + position = HostFileName->Strstr( &DblspaceString ); + + if( position == INVALID_CHNUM || + position == 0 || + HostFileName->QueryChAt( position - 1 ) != '.' ) { + + return FALSE; + } + + HostFileName->SetChAt( '\\', position - 1 ); + + return TRUE; +} + +#if !defined( _READ_WRITE_DOUBLESPACE_ ) + +ULONG +ComputeVirtualSectors( + IN PCVF_HEADER CvfHeader, + IN ULONG HostFileSize + ) +/*++ + +Routine Description: + + This function computes the appropriate number of virtual + sectors for the given Compressed Volume File. Note that + it always uses a ratio of 2. + +Arguments: + + CvfHeader -- Supplies the Compressed Volume File Header. + HostFileSize -- Supplies the size of the host file in bytes. + +Return Value: + + The number of virtual sectors appropriate to this Compressed + Volume File. + +--*/ +{ + CONST DefaultRatio = 2; + ULONG SystemOverheadSectors, SectorsInFile, + VirtualSectors, MaximumSectors, VirtualClusters; + + if( CvfHeader == NULL || + CvfHeader->Bpb.BytesPerSector == 0 || + CvfHeader->Bpb.SectorsPerCluster == 0 ) { + + return 0; + } + + SystemOverheadSectors = CvfHeader->DosBootSectorLbn + + CvfHeader->CvfHeapOffset + + 2; + + SectorsInFile = HostFileSize / CvfHeader->Bpb.BytesPerSector; + + if( SectorsInFile < SystemOverheadSectors ) { + + return 0; + } + + VirtualSectors = (SectorsInFile - SystemOverheadSectors) * DefaultRatio + + CvfHeader->CvfHeapOffset; + + // VirtualSectors cannot result in more that 0xfff8 clusters on + // the volume, nor can it be greater than the volume's maximum + // capacity. + // + VirtualSectors = min( VirtualSectors, + 0xfff8L * CvfHeader->Bpb.SectorsPerCluster ); + + MaximumSectors = (CvfHeader->CvfMaximumCapacity * 1024L * 1024L) / + CvfHeader->Bpb.BytesPerSector; + + VirtualSectors = min( VirtualSectors, MaximumSectors ); + + // To avoid problems with DOS, do not create a volume with + // a number-of-clusters value in the range [0xFEF, 0xFF7]. + // + VirtualClusters = VirtualSectors / CvfHeader->Bpb.SectorsPerCluster; + + if( VirtualClusters >= 0xFEF && VirtualClusters <= 0xFF7 ) { + + VirtualSectors = 0xFEEL * CvfHeader->Bpb.SectorsPerCluster; + } + + return VirtualSectors; +} + +BOOLEAN +CreateCvfHeader( + OUT PCVF_HEADER CvfHeader, + IN ULONG MaximumCapacity + ) +/*++ + +Routine Description: + + This function creates a Compressed Volume File and fills in + the first sector with a valid CVF Header. The number of sectors + in the DOS BPB is set to zero, to indicate that this volume + file is not initialized. + +Arguments: + + CvfHeader -- Receives the created CVF header. + MaximumCapacity -- Supplies the maximum capacity for the + double-space volume, in bytes. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + if( MaximumCapacity % (8L * 1024L * 1024L) ) { + + // The volume maximum capacity must be a multiple of + // eight megabytes. + // + return FALSE; + } + ULONG Sectors, Clusters, Offset, SectorsInBitmap, SectorsInCvfFatExtension; + + // Most of the fields in the DOS BPB have fixed values: + // + CvfHeader->Jump = 0xEB; + CvfHeader->JmpOffset = 0x903c; + + memcpy( CvfHeader->Oem, "MSDBL6.0", 8 ); + + CvfHeader->Bpb.BytesPerSector = DoubleSpaceBytesPerSector; + CvfHeader->Bpb.SectorsPerCluster = DoubleSpaceSectorsPerCluster; + // ReservedSectors computed below. + CvfHeader->Bpb.Fats = DoubleSpaceFats; + CvfHeader->Bpb.RootEntries = DoubleSpaceRootEntries; + CvfHeader->Bpb.Sectors = 0; + CvfHeader->Bpb.Media = DoubleSpaceMediaByte; + // SectorsPerFat computed below. + CvfHeader->Bpb.SectorsPerTrack = DoubleSpaceSectorsPerTrack; + CvfHeader->Bpb.Heads = DoubleSpaceHeads; + CvfHeader->Bpb.HiddenSectors = DoubleSpaceHiddenSectors; + CvfHeader->Bpb.LargeSectors = 0; + + // Compute the number of sectors and clusters for the given + // maximum capacity: + // + Sectors = MaximumCapacity / CvfHeader->Bpb.BytesPerSector; + Clusters = Sectors / CvfHeader->Bpb.SectorsPerCluster; + + // Reserve space for a 16-bit FAT that's big enough for the + // maximum number of clusters. + // + CvfHeader->Bpb.SectorsPerFat = + (2 * Clusters + CvfHeader->Bpb.BytesPerSector - 1)/ + CvfHeader->Bpb.BytesPerSector; + + // DOS 6.2 requires that the first sector of the Sector Heap + // be cluster aligned; since the Root Directory is one cluster, + // this means that ReservedSectors plus SectorsPerFat must be + // a multiple of SectorsPerCluster. + // + CvfHeader->Bpb.ReservedSectors = DoubleSpaceReservedSectors; + + Offset = (CvfHeader->Bpb.ReservedSectors + CvfHeader->Bpb.SectorsPerFat) % + CvfHeader->Bpb.SectorsPerCluster; + + if( Offset != 0 ) { + + CvfHeader->Bpb.ReservedSectors += + CvfHeader->Bpb.SectorsPerCluster - Offset; + } + + // So much for the DOS BPB. Now for the Double Space + // BPB extensions. The location of the CVFFatExtension + // table is preceded by sector zero, the bitmap, and + // one reserved sector. Note that MaximumCapacity must + // be a multiple of 8 Meg (8 * 1024 * 1024), which simplifies + // calculation of SectorsInBitmap, SectorsInCvfFatExtension, + // and CvfBitmap2KSize. + // + SectorsInBitmap = (Sectors / 8) / CvfHeader->Bpb.BytesPerSector; + SectorsInCvfFatExtension = (Clusters * 4) / CvfHeader->Bpb.BytesPerSector; + + CvfHeader->CvfFatExtensionsLbnMinus1 = SectorsInBitmap + 1; + CvfHeader->LogOfBytesPerSector = DoubleSpaceLog2BytesPerSector; + CvfHeader->DosBootSectorLbn = DoubleSpaceReservedSectors2 + + CvfHeader->CvfFatExtensionsLbnMinus1 + 1 + + SectorsInCvfFatExtension; + CvfHeader->DosRootDirectoryOffset = + CvfHeader->Bpb.ReservedSectors + CvfHeader->Bpb.SectorsPerFat; + CvfHeader->CvfHeapOffset = + CvfHeader->DosRootDirectoryOffset + DoubleSpaceSectorsInRootDir; + CvfHeader->CvfFatFirstDataEntry = + CvfHeader->CvfHeapOffset / CvfHeader->Bpb.SectorsPerCluster - 2; + CvfHeader->CvfBitmap2KSize = SectorsInBitmap / DSSectorsPerBitmapPage; + CvfHeader->LogOfSectorsPerCluster = DoubleSpaceLog2SectorsPerCluster; + CvfHeader->Is12BitFat = 1; + + CvfHeader->MinFile = 32L * DoubleSpaceRootEntries + + ( CvfHeader->DosBootSectorLbn + + CvfHeader->Bpb.ReservedSectors + + CvfHeader->Bpb.SectorsPerFat + + CVF_MIN_HEAP_SECTORS ) * + CvfHeader->Bpb.BytesPerSector; + + CvfHeader->CvfMaximumCapacity = (USHORT)(MaximumCapacity/(1024L * 1024L)); + + return TRUE; +} + +BOOLEAN +ZeroFillFile( + IN HANDLE FileHandle, + IN ULONG Size, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This function extends the file to the requested size by + writing zeroes, to ensure that all sectors are writeable. + + BUGBUG billmc -- should it also read the file? + +Arguments: + + FileHandle -- Supplies a handle to the file. + Size -- Supplies the size of the file. + Message -- Supplies an outlet for messages. May be NULL. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER L; + BIG_INT Offset; + HMEM Mem; + NTSTATUS Status; + BOOLEAN Result = TRUE; + ULONG Remainder, BytesToWrite; + PVOID Buffer; + CONST ULONG BufferSize = 32 * 1024; + CONST ULONG BufferAlignment = 0xfff; + ULONG Percent; + + Offset = 0; + Remainder = Size; + + // Align the write buffer on a 4K boundary. + // + if( !Mem.Initialize() || + (Buffer = Mem.Acquire( BufferSize, BufferAlignment )) == NULL ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + memset( Buffer, 0, BufferSize ); + + while( Remainder ) { + + BytesToWrite = ( BufferSize < Remainder ) ? BufferSize : Remainder; + + L = Offset.GetLargeInteger(); + + Status = NtWriteFile( FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + Buffer, + BytesToWrite, + &L, + NULL + ); + + // BUGBUG billmc -- this is a hack... + // + if( Status == STATUS_INVALID_USER_BUFFER ) { + + LARGE_INTEGER DelayInterval; + + // Wait half a second. + // + DelayInterval = RtlConvertLongToLargeInteger( -5000000 ); + NtDelayExecution( TRUE, &DelayInterval ); + + Status = NtWriteFile( FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + Buffer, + BytesToWrite, + &L, + NULL + ); + } + + if( !NT_SUCCESS( Status ) ) { + + Message ? Message->Set( MSG_DBLSPACE_CREATE_DISK_ERROR ) : 1; + Message ? Message->Display( "" ) : 1; + Result = FALSE; + break; + } + + Offset += BytesToWrite; + Remainder -= BytesToWrite; + + if( Message ) { + + // Note that Remainder and Size are both divided by + // SectorSize to avoid ULONG overflow. + // + Percent = (100 * ((Size - Remainder)/DoubleSpaceBytesPerSector))/ + (Size/DoubleSpaceBytesPerSector); + + Message->Set( MSG_PERCENT_COMPLETE ); + if( !Message->Display( "%d", Percent ) ) { + + return FALSE; + } + } + } + + return Result; +} + +BOOLEAN +FileExists( + IN PCWSTRING FileName, + OUT PBOOLEAN Error, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This function determines whether a file exists. + +Arguments: + + FileName -- Supplies the file name. + Error -- Receives TRUE if the function failed due to + an error. + Message -- Supplies an outlet for messages (may be NULL). + +Return Value: + + TRUE upon successful completion. If the function fails because + of an error, *Error is set to TRUE and the function returns FALSE. + Otherwise, the file does not exist. + +--*/ +{ + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE FileHandle; + UNICODE_STRING UnicodeString; + + DbgPtrAssert( FileName ); + DbgPtrAssert( Error ); + + *Error = FALSE; + + UnicodeString.Buffer = (PWSTR)FileName->GetWSTR(); + UnicodeString.Length = FileName->QueryChCount() * sizeof( WCHAR ); + UnicodeString.MaximumLength = UnicodeString.Length; + + InitializeObjectAttributes( &ObjectAttributes, + &UnicodeString, + OBJ_CASE_INSENSITIVE, + 0, + 0 ); + + Status = NtOpenFile( &FileHandle, + FILE_GENERIC_READ, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_NON_DIRECTORY_FILE ); + + NtClose( FileHandle ); + + if( NT_SUCCESS( Status ) ) { + + return TRUE; + } + + if( Status == STATUS_ACCESS_DENIED || + Status == STATUS_SHARING_VIOLATION ) { + + *Error = FALSE; + return TRUE; + } + + if( Status != STATUS_OBJECT_NAME_NOT_FOUND && + Status != STATUS_OBJECT_PATH_NOT_FOUND && + Status != STATUS_NO_SUCH_FILE ) { + + *Error = TRUE; + } + + return FALSE; +} + + + +BOOLEAN +GenerateHostFileName( + IN PCWSTRING HostDriveName, + OUT PWSTRING HostFileName, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This function chooses a name for the host file. Names are of + the form DBLSPACE.xxx; DBLSPACE.000 is reserved for drives + created by compressing an existing drive. + +Arguments: + + HostDriveName -- Supplies the NT name of the host drive. + HostFileName -- Receives the generated name. + Message -- Supplies an outlet for messages. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + ULONG i; + WCHAR NameBuffer[16]; + DSTRING Current, BackSlash; + BOOLEAN Error; + + DbgPtrAssert( HostDriveName ); + DbgPtrAssert( HostFileName ); + + if( !BackSlash.Initialize( "\\" ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + for( i = 1; i < 255; i++ ) { + + swprintf( NameBuffer, (LPCWSTR)L"DBLSPACE.%03d", i ); + + if( !HostFileName->Initialize( NameBuffer ) || + !Current.Initialize( HostDriveName ) || + !Current.Strcat( &BackSlash ) || + !Current.Strcat( HostFileName ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + if( !FileExists( &Current, &Error, Message ) ) { + + return !Error; + } + } + + return FALSE; +} + +BOOLEAN +FatDbDeleteCvf( + IN PCWSTRING CvfFileName, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This method deletes a compressed volume file. + +Arguments: + + CvfFileName -- Supplies the name of the file. + Message -- Supplies an outlet for messages. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING UnicodeString; + FILE_DISPOSITION_INFORMATION DispositionInfo; + HANDLE FileHandle; + NTSTATUS Status; + + UnicodeString.Buffer = (PWSTR)CvfFileName->GetWSTR(); + UnicodeString.Length = CvfFileName->QueryChCount() * sizeof( WCHAR ); + UnicodeString.MaximumLength = UnicodeString.Length; + + InitializeObjectAttributes( &ObjectAttributes, + &UnicodeString, + OBJ_CASE_INSENSITIVE, + 0, + 0 ); + + Status = NtOpenFile( &FileHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_DELETE, + FILE_NON_DIRECTORY_FILE ); + + if( NT_SUCCESS( Status ) ) { + + DispositionInfo.DeleteFile = TRUE; + + Status = NtSetInformationFile( FileHandle, + &IoStatusBlock, + &DispositionInfo, + sizeof( DispositionInfo ), + FileDispositionInformation ); + } + + if( !NT_SUCCESS( Status ) ) { + + if( Status == STATUS_ACCESS_DENIED || + Status == STATUS_SHARING_VIOLATION ) { + + Message->Set( MSG_DASD_ACCESS_DENIED ); + Message->Display( "" ); + + } else { + + Message->Set( MSG_DBLSPACE_CANT_DELETE_CVF ); + Message->Display( "" ); + } + + return FALSE; + } + + NtClose( FileHandle ); + return TRUE; +} + + +BOOLEAN +FAR APIENTRY +FatDbCreateCvf( + IN PCWSTRING HostDriveName, + IN PCWSTRING HostFileName, + IN ULONG Size, + IN OUT PMESSAGE Message, + OUT PWSTRING CreatedName + ) +/*++ + +Routine Description: + + This function creates a Compressed Volume File to + hold a Double Space volume. + +Arguments: + + HostDriveName -- Supplies the NT name of the drive which + will contain the new volume's Compressed + Volume File. + HostFileName -- Supplies the name of the Compressed Volume + File. If this parameter is NULL, this + function will choose a name. + Size -- Supplies the size of the Compressed Volume File. + Message -- Supplies an outlet for messages (may be NULL) + HostFileFullName -- Receives the unqualified name of the + newly-created host file. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + BYTE SectorZeroBuffer[512]; + CVF_HEADER CvfHeader; + PPACKED_CVF_HEADER PackedCvfHeader; + IO_STATUS_BLOCK IoStatusBlock; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING FileNameString; + DSTRING HostFileFullName, BackSlash; + DP_DRIVE HostDrive; + BIG_INT Offset, InitialSize; + LARGE_INTEGER L; + FILE_DISPOSITION_INFORMATION DispositionInfo; + HANDLE FileHandle; + ULONG MaximumCapacity; + ULONG OldAttributes = 0; + NTSTATUS Status; + MSGID MsgId; + + + // Make sure we have a file name. + // + if( HostFileName != NULL ) { + + if( !CreatedName->Initialize( HostFileName ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + } else { + + if( !GenerateHostFileName( HostDriveName, CreatedName, Message ) ) { + + Message ? Message->Set( MSG_DBLSPACE_NO_CVF_NAME ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + } + + // Construct the fully-qualified name of the Compressed Volume File. + // + if( !BackSlash.Initialize( "\\" ) || + !HostFileFullName.Initialize( HostDriveName ) || + !HostFileFullName.Strcat( &BackSlash ) || + !HostFileFullName.Strcat( CreatedName ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + + // The maximum capacity of the new file is based on the + // size of the host drive. Initialize the DP_DRIVE as + // a transient drive, so that it won't keep a handle open. + // + // Note that MaximumCapacity is rounded up to the next + // highest multiple of 8 Meg. + // + if( !HostDrive.Initialize( HostDriveName, Message, FALSE, FALSE ) ) { + + return FALSE; + } + + MaximumCapacity = + ComputeMaximumCapacity( HostDrive.QuerySectors().GetLowPart() * + HostDrive.QuerySectorSize() ); + + MaximumCapacity = ( (MaximumCapacity + EIGHT_MEG - 1 ) / EIGHT_MEG ) * EIGHT_MEG; + + + // Create the file. + // + FileNameString.Buffer = (PWSTR)HostFileFullName.GetWSTR(); + FileNameString.Length = HostFileFullName.QueryChCount() * sizeof( WCHAR ); + FileNameString.MaximumLength = FileNameString.Length; + + InitializeObjectAttributes( + &ObjectAttributes, + &FileNameString, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + + InitialSize = Size; + L = InitialSize.GetLargeInteger(); + + Status = NtCreateFile( &FileHandle, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + &ObjectAttributes, + &IoStatusBlock, + &L, + FILE_ATTRIBUTE_NORMAL, + 0, // No sharing. + FILE_CREATE, + FILE_NON_DIRECTORY_FILE | FILE_WRITE_THROUGH, + NULL, // No EA's + 0 + ); + + if( !NT_SUCCESS( Status ) ) { + + if( Message ) { + + if( Status == STATUS_OBJECT_NAME_COLLISION || + Status == STATUS_FILE_IS_A_DIRECTORY ) { + + MsgId = MSG_DBLSPACE_CVF_NAME_EXISTS; + + } else if ( Status == STATUS_DISK_FULL ) { + + MsgId = MSG_DBLSPACE_NO_SPACE_ON_HOST; + + } else { + + MsgId = MSG_DBLSPACE_CVF_CREATE_ERROR; + } + + Message->Set( MsgId ); + Message->Display( "" ); + } + + return FALSE; + } + + // Fill the file with zeroes. + // + if( !ZeroFillFile( FileHandle, Size, Message ) ) { + + DispositionInfo.DeleteFile = TRUE; + + NtSetInformationFile( FileHandle, + &IoStatusBlock, + &DispositionInfo, + sizeof( DispositionInfo ), + FileDispositionInformation ); + + NtClose( FileHandle ); + return FALSE; + } + + // Create the Compressed Volume File Header: + // + if( !CreateCvfHeader( &CvfHeader, MaximumCapacity ) ) { + + DispositionInfo.DeleteFile = TRUE; + + NtSetInformationFile( FileHandle, + &IoStatusBlock, + &DispositionInfo, + sizeof( DispositionInfo ), + FileDispositionInformation ); + + NtClose( FileHandle ); + return FALSE; + } + + // Now fill in the value of Virtual Sectors. + // + CvfHeader.Bpb.LargeSectors = ComputeVirtualSectors( &CvfHeader, Size ); + + // Fill in the packed CVF Header and write it to the + // newly-created file. + // + memset( SectorZeroBuffer, 0, 512 ); + + PackedCvfHeader = (PPACKED_CVF_HEADER)SectorZeroBuffer; + + CvfPackCvfHeader( PackedCvfHeader, &CvfHeader ); + + + Offset = 0; + L = Offset.GetLargeInteger(); + + Status = NtWriteFile( FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + SectorZeroBuffer, + 512, + &L, + NULL ); + + if( !NT_SUCCESS( Status ) ) { + + DispositionInfo.DeleteFile = TRUE; + + NtSetInformationFile( FileHandle, + &IoStatusBlock, + &DispositionInfo, + sizeof( DispositionInfo ), + FileDispositionInformation ); + + NtClose( FileHandle ); + + Message ? Message->Set( MSG_DBLSPACE_CREATE_DISK_ERROR ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + NtClose( FileHandle ); + + IFS_SYSTEM::FileSetAttributes( &HostFileFullName, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, + &OldAttributes ); + + return TRUE; +} +#endif + + +BOOLEAN +FAR APIENTRY +FatDbCreate( + IN PCWSTRING HostDriveName, + IN PCWSTRING HostFileName, + IN ULONG Size, + IN OUT PMESSAGE Message, + IN PCWSTRING LabelString, + OUT PWSTRING CreatedName + ) +/*++ + +Routine Description: + + This function creates and formats a Double Space volume. + +Arguments: + + HostDriveName -- Supplies the name of the host drive + HostFileName -- Supplies the unqualified file name for + the new Compressed Volume File. May be + NULL, in which case this function chooses + a name. + Size -- Supplies the initial size of the Compressed + Volume File. + LabelString -- Supplies the volume label. May be NULL. + Message -- Supplies an outlet for messages. + CreatedFile -- Receives the unqualified name of the + newly-created host file. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ +#if !defined( _READ_WRITE_DOUBLESPACE_ ) + + return FALSE; + +#else // _READ_WRITE_DOUBLESPACE_ + + FATDB_VOL FatDbVol; + DSTRING FatString, FileSystemName, HostFileFullName, BackSlash; + + + // Make sure that the host volume is FAT. + // + if( !FatString.Initialize( "FAT" ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + if( !IFS_SYSTEM::QueryFileSystemName( HostDriveName, &FileSystemName ) || + FileSystemName.Strcmp( &FatString ) != 0 ) { + + Message ? Message->Set( MSG_DBLSPACE_HOST_NOT_FAT ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + // Create the Compressed Volume File: + // + if( !FatDbCreateCvf( HostDriveName, + HostFileName, + Size, + Message, + CreatedName ) ) { + + return FALSE; + } + + // Initialize a FATDB_VOL and Format it. + // + if( !BackSlash.Initialize( "\\" ) || + !HostFileFullName.Initialize( HostDriveName ) || + !HostFileFullName.Strcat( &BackSlash ) || + !HostFileFullName.Strcat( CreatedName ) ) { + + Message ? Message->Set( MSG_FMT_NO_MEMORY ) : 1; + Message ? Message->Display( "" ) : 1; + return FALSE; + } + + if( !FatDbVol.Initialize( NULL, + &HostFileFullName, + Message ) ) { + + return FALSE; + } + + return( FatDbVol.Format( LabelString, + Message, + 0, + 0 ) ); +#endif +} + +BOOLEAN +FAR APIENTRY +FatDbDelete( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This function deletes a mounted Double Space drive. + +Arguments: + + NtDriveName -- Supplies the NT name of the compressed drive. + Message -- Supplies an outlet for messages. + +Return Value: + + TRUE upon successful completion. + +--*/ +{ +#if !defined( _READ_WRITE_DOUBLESPACE_ ) + + return FALSE; + +#else // _READ_WRITE_DOUBLESPACE_ + + DSTRING HostFileName; + DSTRING FileSystemName; + BOOLEAN IsCompressed; + PLOG_IO_DP_DRIVE Drive; + + if( (Drive = NEW LOG_IO_DP_DRIVE) == NULL ) { + + Message->Set( MSG_FMT_NO_MEMORY ); + Message->Display( "" ); + return FALSE; + } + + if( !Drive->Initialize( NtDriveName, Message ) || + !Drive->QueryMountedFileSystemName( &FileSystemName, &IsCompressed ) || + !IsCompressed ) { + + DELETE( Drive ); + Message->Set( MSG_DBLSPACE_NOT_COMPRESSED_NO_NAME ); + Message->Display( "" ); + return FALSE; + } + + if( !Drive->Lock() ) { + + DELETE( Drive ); + Message->Set( MSG_CANT_LOCK_THE_DRIVE ); + Message->Display( "" ); + return FALSE; + } + + + if( !QueryHostFileName( NtDriveName, &HostFileName ) ) { + + DELETE( Drive ); + Message->Set( MSG_DBLSPACE_CANT_GET_CVF_NAME ); + Message->Display( "" ); + return FALSE; + } + + // Delete the drive object to dismount the drive and + // close its handle. + // + DELETE( Drive ); + + Message->Set( MSG_DBLSPACE_DISMOUNTED_NO_NAME ); + Message->Display( "" ); + + // + // Delete the host file. + // + return( FatDbDeleteCvf( &HostFileName, Message ) ); + return TRUE; +#endif +} + + +BOOLEAN +FAR APIENTRY +FatDbFormat( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Quick, + IN MEDIA_TYPE MediaType, + IN PCWSTRING LabelString, + IN ULONG ClusterSize + ) +/*++ + +Routine Description: + + This function formats an existing, mounted Compressed Volume. + +Arguments: + + NtDriveName - Supplies the NT style drive name of the volume to format. + Message - Supplies an outlet for messages. + Quick - Supplies whether or not to do a quick format. + This parameter is ignored; Double-Space format + always uses quick format. + MediaType - Supplies the media type of the drive. + This parameter is ignored; Double-Space always + uses a media byte of 0xf8. + LabelString - Supplies a volume label to be set on the volume after + format. + ClusterSize - This parameter is ignored. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if !defined( _READ_WRITE_DOUBLESPACE_ ) + + return FALSE; + +#else // _READ_WRITE_DOUBLESPACE_ + + FATDB_VOL FatDbVol; + PDP_DRIVE DpDrive; + DSTRING HostFileName; + + UNREFERENCED_PARAMETER( Quick ); + UNREFERENCED_PARAMETER( MediaType ); + UNREFERENCED_PARAMETER( ClusterSize ); + + if( !QueryHostFileName( NtDriveName, &HostFileName ) ) { + + Message->Set( MSG_DBLSPACE_CANT_GET_CVF_NAME ); + Message->Display( "" ); + return FALSE; + } + + return( !FatDbVol.Initialize( NtDriveName, + &HostFileName, + Message, + FALSE ) && + !FatDbVol.Format( LabelString, Message, ClusterSize ) ); +#endif +} + +BOOLEAN +FAR APIENTRY +FatDbChkdsk( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Fix, + IN BOOLEAN Verbose, + IN BOOLEAN OnlyIfDirty, + IN BOOLEAN Recover, + IN PCWSTRING PathToCheck + ) +{ + FATDB_VOL FatDbVol; + + UNREFERENCED_PARAMETER(PathToCheck); + + return (!FatDbVol.Initialize(NtDriveName, + PathToCheck, + Message, + FALSE ) || /* ExclusiveWrite */ + !FatDbVol.ChkDsk(Fix ? TotalFix : CheckOnly, + Message, + Verbose, + OnlyIfDirty, + Recover, + Recover + ) ); +} diff --git a/private/utils/ufat/src/eaheader.cxx b/private/utils/ufat/src/eaheader.cxx new file mode 100644 index 000000000..cbf1ed807 --- /dev/null +++ b/private/utils/ufat/src/eaheader.cxx @@ -0,0 +1,215 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" + + +DEFINE_EXPORTED_CONSTRUCTOR( EA_HEADER, CLUSTER_CHAIN, UFAT_EXPORT ); + +VOID +EA_HEADER::Construct ( + ) +/*++ + +Routine Description: + + Constructor for EA_HEADER. Initializes private data to default values. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _ht = NULL; + _off_tab_size = 0; +} + + +UFAT_EXPORT +EA_HEADER::~EA_HEADER( + ) +/*++ + +Routine Description: + + Destructor for EA_HEADER. Frees memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +EA_HEADER::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT StartingCluster, + IN USHORT LengthOfChain + ) +/*++ + +Routine Description: + + This routine prepares this object for reads and writes. It also + prepares the object with memory so that it can accept queries. + + If the length of the cluster chain is not specified this routine + will read in the first cluster in order to compute the actual + length of the cluster chain. + +Arguments: + + Mem - Supplies the memory for the cluster chain. + Drive - Supplies the drive that contains the EA file. + FatSuperArea - Supplies information about the FAT file system. + Fat - Supplies the file allocation table. + StartingCluster - Supplies the first cluster of the EA file. + LengthOfChain - Supplies the number of clusters necessary to contain + the EA file header, base table and offset table. + +Return Value: + + FALSE - Initialization failed. + TRUE - Initialization succeeded. + +--*/ +{ + HMEM hmem; + INT i; + + Destroy(); + + // See if the length of the chain needs to be computed. + if (!LengthOfChain) { + if (!hmem.Initialize() || + !CLUSTER_CHAIN::Initialize(&hmem, Drive, FatSuperArea, Fat, + StartingCluster, 1) || + !(_ht = (PEA_HEADER_AND_TABLE) GetBuf()) || + !CLUSTER_CHAIN::Read() || + !(_ht->Header.Signature == HeaderSignature) || + !(_ht->Header.FormatType == 0) || + !(_ht->Header.LogType == 0)) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + for (i = 0; i < BaseTableSize && !_ht->Table.BaseTab[i]; i++) { + } + if (i == BaseTableSize) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + LengthOfChain = _ht->Table.BaseTab[i]; + } + + if (!CLUSTER_CHAIN::Initialize(Mem, Drive, FatSuperArea, Fat, + StartingCluster, LengthOfChain) || + !(_ht = (PEA_HEADER_AND_TABLE) GetBuf())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + // Compute the number of offset table entries. + _off_tab_size = Drive->QuerySectorSize()* + FatSuperArea->QuerySectorsPerCluster()* + LengthOfChain; + _off_tab_size -= sizeof(EA_FILE_HEADER); + _off_tab_size -= BaseTableSize*sizeof(USHORT); + _off_tab_size /= sizeof(USHORT); + + if (_off_tab_size < 0) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + return TRUE; +} + + +UFAT_EXPORT +USHORT +EA_HEADER::QueryEaSetClusterNumber( + IN USHORT Handle + ) CONST +/*++ + +Routine Description: + + This function computes the EA cluster number for an EA set in the + EA file. This function will return 0 if the handle is invalid or + outside the range of the table. + +Arguments: + + Handle - Supplies the handle for the desired EA set. + +Return Value: + + Returns the EA cluster number for the EA set whose handle is 'Handle'. + +--*/ +{ + USHORT off; + + if (!_ht) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return 0; + } + + Handle = (( Handle << 1 ) >> 1 ); + + if ((LONG)Handle >= _off_tab_size || + (off = _ht->Table.OffTab[Handle]) == InvalidHandle) { + return 0; + } + + return (( off << 1 ) >> 1 ) + _ht->Table.BaseTab[Handle>>7]; +} + + +VOID +EA_HEADER::Destroy( + ) +/*++ + +Routine Description: + + This routine puts this object in a blank state. It is not necessary + to call this routine between calls to Init because Init calls this + routine automatically. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _ht = NULL; + _off_tab_size = 0; +} diff --git a/private/utils/ufat/src/easet.cxx b/private/utils/ufat/src/easet.cxx new file mode 100644 index 000000000..11b63c76b --- /dev/null +++ b/private/utils/ufat/src/easet.cxx @@ -0,0 +1,404 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" + + +DEFINE_EXPORTED_CONSTRUCTOR( EA_SET, CLUSTER_CHAIN, UFAT_EXPORT ); + +VOID +EA_SET::Construct ( + ) +/*++ + +Routine Description: + + Constructor for EA_SET. Sets private data to default values. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + memset(&_eahdr, 0, sizeof(_eahdr)); + _size = 0; + _size_imposed = FALSE; + _current_ea = NULL; + _current_index = 0; +} + + +UFAT_EXPORT +EA_SET::~EA_SET( + ) +/*++ + +Routine Description: + + Destructor for EA_SET. Frees memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +EA_SET::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT ClusterNumber, + IN USHORT LengthOfChain + ) +/*++ + +Routine Description: + + This routine initialize the EA_SET to model the EA set which resides + at FAT cluster 'ClusterNumber'. + +Arguments: + + Mem - Supplies the memory for the cluster chain. + Drive - Supplies the drive where the EA set is contained. + FatSuperArea - Supplies the important drive parameters. + Fat - Supplies the file allocation table. + StartingCluster - Supplies the starting cluster of the EA set. + LengthOfChain - Supplies the length of the cluster chai which contains + the EA set. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + HMEM hmem; + ULONG cluster_size; + ULONG sector_size; + + Destroy(); + + if (!FatSuperArea || !Drive || !(sector_size = Drive->QuerySectorSize())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + cluster_size = sector_size*FatSuperArea->QuerySectorsPerCluster(); + + if (!LengthOfChain) { + if (!hmem.Initialize() || + !CLUSTER_CHAIN::Initialize(&hmem, Drive, FatSuperArea, Fat, + ClusterNumber, 1) || + !Read()) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + _size = _eahdr.TotalSize + SizeOfEaHdr - sizeof(LONG); + _size_imposed = TRUE; + + if (_size%cluster_size) { + LengthOfChain = (USHORT) (_size/cluster_size + 1); + } else { + LengthOfChain = (USHORT) (_size/cluster_size); + } + } else { + _size = cluster_size*LengthOfChain; + _size_imposed = FALSE; + } + + + if (!CLUSTER_CHAIN::Initialize(Mem, Drive, FatSuperArea, Fat, + ClusterNumber, LengthOfChain)) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + return TRUE; +} + + +UFAT_EXPORT +BOOLEAN +EA_SET::Read( + ) +/*++ + +Routine Description: + + This routine reads the cluster chain and then unpacks the ea header. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + LONG size; + + + if (!CLUSTER_CHAIN::Read() || !UnPackEaHeader()) { + return FALSE; + } + + size = _eahdr.TotalSize + SizeOfEaHdr - sizeof(LONG); + if (size < _size) { + _size = size; + _size_imposed = TRUE; + } + + return TRUE; +} + + +UFAT_EXPORT +PEA +EA_SET::GetEa( + IN ULONG Index, + OUT PLONG EaSize, + OUT PBOOLEAN PossiblyMore + ) +/*++ + +Routine Description: + + This routine returns a pointer to the Index'th EA. An Index of 0 + indicates the first EA and so on. A NULL pointer will be returned if + the Index'th EA does not exist. + + This routine will validate the EA before returning it. If the EA is + invalid then NULL will be returned. + + The return value 'PossiblyMore' will only be computed in the event + that the EA at index 'Index' can't be found. It is used to indicate + that there may be another EA in the next cluster of the cluster chain. + +Arguments: + + Index - Supplies which EA is requested. + EaSize - Returns the size of the EA. + PossiblyMore - Returns TRUE if there may possibly be more EAs in + a cluster beyond the boundary of the cluster chain. + Returns FALSE if this is impossible. + +Return Value: + + A pointer to an EA structure or NULL. + +--*/ +{ + ULONG i; + PEA r; + PCHAR p, b; + ULONG offset; + + if (PossiblyMore) { + *PossiblyMore = FALSE; + } + + if (!(b = (PCHAR) GetBuf())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return NULL; + } + + if (!_current_ea || Index < _current_index) { + p = (PCHAR) (r = (PEA) (b + SizeOfEaHdr)); + + if (!r->NameSize || !r->ValueSize[0] && !r->ValueSize[1]) { + return NULL; + } + + offset = sizeof(EA) + r->NameSize + r->ValueSize[0] + + (r->ValueSize[1]<<8); + + if (p - b + offset > (ULONG)_size) { + if (PossiblyMore && !_size_imposed) { + *PossiblyMore = TRUE; + } + + return NULL; + } + + if (p[sizeof(EA) + r->NameSize - 1]) { + return NULL; + } + + _current_index = 0; + } else { + p = (PCHAR) (r = _current_ea); + + offset = sizeof(EA) + r->NameSize + r->ValueSize[0] + + (r->ValueSize[1]<<8); + } + + for (i = _current_index; i < Index; i++) { + r = (PEA) (p += offset); + + if (p - b + sizeof(EA) > (ULONG)_size) { + if (PossiblyMore && !_size_imposed) { + *PossiblyMore = TRUE; + } + + return NULL; + } + + if (!r->NameSize || !r->ValueSize[0] && !r->ValueSize[1]) { + return NULL; + } + + offset = sizeof(EA) + r->NameSize + r->ValueSize[0] + + (r->ValueSize[1]<<8); + + if (p - b + offset > (ULONG)_size) { + if (PossiblyMore && !_size_imposed) { + *PossiblyMore = TRUE; + } + + return NULL; + } + + if (p[sizeof(EA) + r->NameSize - 1]) { + return NULL; + } + } + + _current_index = i; + _current_ea = r; + + if (EaSize) { + *EaSize = offset; + } + + return r; +} + + +VOID +EA_SET::Destroy( + ) +/*++ + +Routine Description: + + This routine returns the object to its initial state. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + memset(&_eahdr, 0, sizeof(_eahdr)); + _size = 0; + _size_imposed = FALSE; + _current_ea = NULL; + _current_index = 0; +} + + +BOOLEAN +EA_SET::PackEaHeader( + ) +/*++ + +Routine Description: + + This routine packs the EA set header. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + PPACKED_EA_HDR peahdr; + + if (!(peahdr = (PPACKED_EA_HDR) GetBuf())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return FALSE; + } + + peahdr->Signature = _eahdr.Signature; + peahdr->OwnHandle = _eahdr.OwnHandle; + peahdr->NeedCount = _eahdr.NeedCount; + memcpy(peahdr->OwnerFileName, _eahdr.OwnerFileName, 14); + memcpy(peahdr->Reserved, &_eahdr.Reserved, sizeof(ULONG)); + memcpy(peahdr->TotalSize, &_eahdr.TotalSize, sizeof(LONG)); + + return TRUE; +} + + +BOOLEAN +EA_SET::UnPackEaHeader( + ) +/*++ + +Routine Description: + + This routine unpacks the EA set header. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + PPACKED_EA_HDR peahdr; + + if (!(peahdr = (PPACKED_EA_HDR) GetBuf())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return FALSE; + } + + _eahdr.Signature = peahdr->Signature; + _eahdr.OwnHandle = peahdr->OwnHandle; + _eahdr.NeedCount = peahdr->NeedCount; + memcpy(_eahdr.OwnerFileName, peahdr->OwnerFileName, 14); + memcpy(&_eahdr.Reserved, peahdr->Reserved, sizeof(ULONG)); + memcpy(&_eahdr.TotalSize, peahdr->TotalSize, sizeof(LONG)); + + return TRUE; +} diff --git a/private/utils/ufat/src/entry.cxx b/private/utils/ufat/src/entry.cxx new file mode 100644 index 000000000..2e6c99bc8 --- /dev/null +++ b/private/utils/ufat/src/entry.cxx @@ -0,0 +1,496 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + entry.cxx + +Abstract: + + This module contains the entry points for UFAT.DLL. These + include: + + Chkdsk + Format + +Author: + + Bill McJohn (billmc) 31-05-91 + +Environment: + + ULIB, User Mode + +--*/ + +#include <pch.cxx> + +#include "error.hxx" +#include "path.hxx" +#include "ifssys.hxx" +#include "filter.hxx" +#include "system.hxx" +#include "dir.hxx" +#include "rcache.hxx" +#ifdef DBLSPACE_ENABLED +#include "dblentry.hxx" +#endif // DBLSPACE_ENABLED + +extern "C" { + #include "nturtl.h" +} + +#include "message.hxx" +#include "rtmsg.h" +#include "ifsserv.hxx" +#include "ifsentry.hxx" + + +VOID +ReportFileNotFoundError( + IN PPATH PathToCheck, + IN OUT PMESSAGE Message + ) +{ + PWSTRING dirs_and_name; + + if (dirs_and_name = PathToCheck->QueryDirsAndName()) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", dirs_and_name); + DELETE(dirs_and_name); + } +} + + +BOOLEAN +FAR APIENTRY +Chkdsk( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Fix, + IN BOOLEAN Verbose, + IN BOOLEAN OnlyIfDirty, + IN BOOLEAN Recover, + IN PPATH PathToCheck, + IN BOOLEAN Extend, + IN BOOLEAN ResizeLogFile, + IN ULONG LogFileSize, + IN PULONG ExitStatus + ) +/*++ + +Routine Description: + + Check a FAT volume. + +Arguments: + + DosDrivName supplies the name of the drive to check + Message supplies an outlet for messages + Fix TRUE if Chkdsk should fix errors + Verbose TRUE if Chkdsk should list every file it finds + OnlyIfDirty TRUE if the drive should be checked only if + it is dirty + Recover TRUE if Chkdsk should verify all of the sectors + on the disk. + PathToCheck Supplies a path to files Chkdsk should check + for contiguity + Extend Unused (should always be FALSE) + ExitStatus Returns exit status to chkdsk.exe + +Return Value: + + TRUE if successful. + +--*/ +{ + FAT_VOL FatVol; + BOOLEAN r; + PWSTRING dir_name; + PWSTRING name; + PWSTRING prefix_name; + FSN_FILTER filter; + PFSN_DIRECTORY directory; + PARRAY file_array; + PDSTRING files_to_check; + ULONG num_files; + ULONG i; + PFSNODE fsnode; + PATH dir_path; + DSTRING backslash; + PPATH full_path; + PREAD_CACHE read_cache; + ULONG exit_status; + + if (NULL == ExitStatus) { + ExitStatus = &exit_status; + } + + if (Extend || !FatVol.Initialize(NtDriveName, Message)) { + *ExitStatus = CHKDSK_EXIT_COULD_NOT_CHK; + return FALSE; + } + + if (Fix && !FatVol.Lock()) { + + if (FatVol.IsFloppy()) { + + Message->Set(MSG_CANT_LOCK_THE_DRIVE); + Message->Display(); + + } else { + + // The client wants to fix the drive, but we can't lock it. + // Offer to fix it on next reboot. + // + Message->Set(MSG_CHKDSK_ON_REBOOT_PROMPT); + Message->Display(""); + + if( Message->IsYesResponse( FALSE ) ) { + + if( FatVol.ForceAutochk( Recover, FALSE, 0, NtDriveName ) ) { + + Message->Set(MSG_CHKDSK_SCHEDULED); + Message->Display(); + + } else { + + Message->Set(MSG_CHKDSK_CANNOT_SCHEDULE); + Message->Display(); + } + } + } + + *ExitStatus = CHKDSK_EXIT_COULD_NOT_CHK; + + return FALSE; + } + + // Try to enable caching, if there's not enough resources then + // just run without a cache. + + if ((read_cache = NEW READ_CACHE) && + read_cache->Initialize(&FatVol, 75)) { + + FatVol.SetCache(read_cache); + + } else { + DELETE(read_cache); + } + + + r = FatVol.ChkDsk( Fix ? TotalFix : CheckOnly, + Message, + Verbose, + OnlyIfDirty, + Recover, + Recover, + FALSE, + 0, + ExitStatus ); + + if (!r) { + return FALSE; + } + + if (PathToCheck) { + + if (!(name = PathToCheck->QueryName()) || + name->QueryChCount() == 0) { + + DELETE(name); + return TRUE; + } + + if (!(full_path = PathToCheck->QueryFullPath())) { + + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + DELETE(name); + return FALSE; + } + + if (!FatVol.Initialize(NtDriveName, Message)) { + DELETE(full_path); + DELETE(name); + return FALSE; + } + + if (!(prefix_name = full_path->QueryPrefix()) || + !dir_path.Initialize(prefix_name)) { + + ReportFileNotFoundError(full_path, Message); + DELETE(name); + DELETE(prefix_name); + DELETE(full_path); + return FALSE; + } + + if (!(directory = SYSTEM::QueryDirectory(&dir_path)) || + !filter.Initialize() || + !filter.SetFileName(name)) { + + ReportFileNotFoundError(full_path, Message); + DELETE(name); + DELETE(prefix_name); + DELETE(directory); + DELETE(full_path); + return FALSE; + } + + DELETE(prefix_name); + + if (!(file_array = directory->QueryFsnodeArray(&filter))) { + + ReportFileNotFoundError(full_path, Message); + DELETE(name); + DELETE(directory); + DELETE(full_path); + return FALSE; + } + + DELETE(directory); + + if (!(num_files = file_array->QueryMemberCount())) { + + ReportFileNotFoundError(full_path, Message); + DELETE(name); + DELETE(directory); + file_array->DeleteAllMembers(); + DELETE(file_array); + DELETE(full_path); + return FALSE; + } + + DELETE(name); + + if (!(files_to_check = NEW DSTRING[num_files])) { + + ReportFileNotFoundError(full_path, Message); + file_array->DeleteAllMembers(); + DELETE(file_array); + DELETE(full_path); + return FALSE; + } + + for (i = 0; i < num_files; i++) { + + fsnode = (PFSNODE) file_array->GetAt(i); + + if (!(name = fsnode->QueryName()) || + !files_to_check[i].Initialize(name)) { + + ReportFileNotFoundError(full_path, Message); + DELETE(name); + file_array->DeleteAllMembers(); + DELETE(file_array); + DELETE(files_to_check); + DELETE(full_path); + return FALSE; + } + + DELETE(name); + } + + file_array->DeleteAllMembers(); + DELETE(file_array); + + if (!(dir_name = full_path->QueryDirs())) { + + if (!backslash.Initialize("\\")) { + ReportFileNotFoundError(full_path, Message); + DELETE(files_to_check); + DELETE(full_path); + return FALSE; + } + } + + r = FatVol.ContiguityReport(dir_name ? dir_name : &backslash, + files_to_check, + num_files, + Message); + + DELETE(files_to_check); + DELETE(dir_name); + DELETE(full_path); + } + + return r; +} + + +BOOLEAN +FAR APIENTRY +Format( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN Quick, + IN MEDIA_TYPE MediaType, + IN PCWSTRING LabelString, + IN ULONG ClusterSize + ) +/*++ + +Routine Description: + + This routine formats a volume for the FAT file system. + +Arguments: + + NtDriveName - Supplies the NT style drive name of the volume to format. + Message - Supplies an outlet for messages. + Quick - Supplies whether or not to do a quick format. + MediaType - Supplies the media type of the drive. + LabelString - Supplies a volume label to be set on the volume after + format. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + PDP_DRIVE DpDrive; + FAT_VOL FatVol; + BIG_INT Sectors; + DSTRING FileSystemName; + BOOLEAN IsCompressed; + + + // Make sure the cluster size switch wasn't specified. + // + if (ClusterSize > 0 && ClusterSize <= 4096) { + Message->Set(MSG_FMT_VARIABLE_CLUSTERS_NOT_SUPPORTED); + Message->Display("%s", "FAT"); + return FALSE; + } + + if( !(DpDrive = NEW DP_DRIVE) ) { + + Message->Set( MSG_FMT_NO_MEMORY ); + Message->Display( "" ); + return FALSE; + } + + if (!DpDrive->Initialize(NtDriveName, Message, TRUE)) { + DELETE( DpDrive ); + return FALSE; + } + + // Check to see if the volume is too large and determine + // whether it's compressed. Volumes larger than 4GB are + // not supported by fastfat. + // + + Sectors = DpDrive->QuerySectors(); + + if (Sectors.GetHighPart() != 0 || + (Sectors * DpDrive->QuerySectorSize()).GetHighPart() != 0 || + FAT_SA::ComputeSecClus(Sectors.GetLowPart(), + LARGE, MediaType) > MaxSecPerClus) { + + DELETE( DpDrive ); + Message->Set(MSG_DISK_TOO_LARGE_TO_FORMAT); + Message->Display(); + return FALSE; + } + +#ifdef DBLSPACE_ENABLED + // Note that we don't care about the return value from + // QueryMountedFileSystemName, just whether the volume + // is compressed. + // + DpDrive->QueryMountedFileSystemName( &FileSystemName, &IsCompressed ); +#endif // DBLSPACE_ENABLED + + // Delete the DP_DRIVE object so it's handle will go away. + // + DELETE( DpDrive ); + + + // Volume is not too large, proceed with format. + +#ifdef DBLSPACE_ENABLED + if( IsCompressed ) { + + return FatDbFormat( NtDriveName, + Message, + Quick, + MediaType, + LabelString, + ClusterSize ); + } +#endif // DBLSPACE_ENABLED + + return( FatVol.Initialize( NtDriveName, + Message, + FALSE, + !Quick, + MediaType ) && + FatVol.Format( LabelString, Message, ClusterSize ) ); +} + + +BOOLEAN +FAR APIENTRY +Recover( + IN PPATH RecFilePath, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + Recover a file on a FAT disk. + +Arguments: + +Return Value: + + TRUE if successful. + +--*/ +{ + FAT_VOL FatVol; + PWSTRING fullpathfilename; + PWSTRING dosdrive; + DSTRING ntdrive; + + fullpathfilename = RecFilePath->QueryDirsAndName(); + dosdrive = RecFilePath->QueryDevice(); + if (!dosdrive || + !IFS_SYSTEM::DosDriveNameToNtDriveName(dosdrive, &ntdrive)) { + DELETE(dosdrive); + DELETE(fullpathfilename); + return FALSE; + } + + if (!fullpathfilename) { + DELETE(dosdrive); + DELETE(fullpathfilename); + return FALSE; + } + + Message->Set(MSG_RECOV_BEGIN); + Message->Display("%W", dosdrive); + Message->WaitForUserSignal(); + + if (!FatVol.Initialize(&ntdrive, Message)) { + DELETE(dosdrive); + DELETE(fullpathfilename); + return FALSE; + } + + if (!FatVol.Recover(fullpathfilename, Message)) { + DELETE(dosdrive); + DELETE(fullpathfilename); + return FALSE; + } + + DELETE(dosdrive); + DELETE(fullpathfilename); + return TRUE; +} + diff --git a/private/utils/ufat/src/fat.cxx b/private/utils/ufat/src/fat.cxx new file mode 100644 index 000000000..8407885f8 --- /dev/null +++ b/private/utils/ufat/src/fat.cxx @@ -0,0 +1,1024 @@ +#include <pch.cxx> + +#define _NTAPI_ULIB_ +#define _UFAT_MEMBER_ + +#include "ulib.hxx" +#include "ufat.hxx" + +#include "bitvect.hxx" +#include "error.hxx" +#include "fat.hxx" + + +DEFINE_CONSTRUCTOR( FAT, SECRUN ); + + +FAT::~FAT( + ) +/*++ + +Routine Description: + + Destructor for FAT. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +VOID +FAT::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FAT. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _fat = NULL; + _num_entries = 0; + _is_big = FALSE; + _low_end_of_chain = 0; + _end_of_chain = 0; + _bad_cluster = 0; + _low_reserved = 0; + _high_reserved = 0; +} + + +VOID +FAT::Destroy( + ) +/*++ + +Routine Description: + + This routine returns a FAT object to its initial state. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _fat = NULL; + _num_entries = 0; + _is_big = FALSE; + _low_end_of_chain = 0; + _end_of_chain = 0; + _bad_cluster = 0; + _low_reserved = 0; + _high_reserved = 0; +} + + +BOOLEAN +FAT::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartSector, + IN USHORT NumberOfEntries, + IN USHORT NumSectors + ) +/*++ + +Routine Description: + + This routine initialize a FAT object. + +Arguments: + + Mem - Supplies the memory for the run of sectors. + Drive - Supplies the drive to read and write from. + StartSector - Supplies the start of the fat. + NumberOfEntries - Supplies the number of entries in the FAT. + NumSectors - Supplies the number of sectors allocated for + the fat. If this parameter is not supplied + then this routine will compute this value + from the given number of entries. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +Notes: + + The 'NumFatSectors' parameter is added to this function + DOS FORMAT does not always make the FAT large enough for + the volume. If this parameter is supported then the + number of entries supported by this FAT will be the lesser + or the actual number passed in that the maximum number that + the given FAT size will support. + +--*/ +{ + SECTORCOUNT n; + ULONG sector_size; + ULONG max_num_entries; + + DebugAssert(Mem); + DebugAssert(Drive); + + Destroy(); + + if (!(sector_size = Drive->QuerySectorSize())) { + Destroy(); + return FALSE; + } + + _num_entries = NumberOfEntries; + _is_big = (BOOLEAN) (_num_entries >= FirstDiskCluster + MaxNumClusForSmallFat); + + if (_is_big) { + _low_end_of_chain = 0xFFF8; + _end_of_chain = 0xFFFF; + _bad_cluster = 0xFFF7; + _low_reserved = 0xFFF0; + _high_reserved = 0xFFF6; + + n = (_num_entries*2 - 1)/sector_size + 1; + + } else { + _low_end_of_chain = 0x0FF8; + _end_of_chain = 0x0FFF; + _bad_cluster = 0x0FF7; + _low_reserved = 0x0FF0; + _high_reserved = 0x0FF6; + + n = (_num_entries*3 - 1)/2/sector_size + 1; + } + + if (NumSectors) { + n = NumSectors; + if (_is_big) { + max_num_entries = (n*sector_size/2); + } else { + max_num_entries = (n*sector_size*2/3); + } + _num_entries = (USHORT)min(_num_entries, max_num_entries); + } + + if (!SECRUN::Initialize(Mem, Drive, StartSector, n)) { + Destroy(); + return FALSE; + } + + _fat = GetBuf(); + + return TRUE; +} + + +UFAT_EXPORT +USHORT +FAT::Index12( + IN USHORT ClusterNumber + ) CONST +/*++ + +Routine Description: + + This routine indexes the FAT as 12 bit little endian entries. + +Arguments: + + ClusterNumber - Supplies the FAT entry desired. + +Return Value: + + The value of the FAT entry at ClusterNumber. + +--*/ +{ + ULONG n; + PUCHAR p; + + p = (PUCHAR) _fat; + + DebugAssert(p); + + n = ClusterNumber*3; + if (n%2) { + return (p[n/2]>>4) | (p[n/2 + 1]<<4); + } else { + return p[n/2] | ((p[n/2 + 1]&0x0F)<<8); + } +} + + +UFAT_EXPORT +VOID +FAT::Set12( + IN USHORT ClusterNumber, + IN USHORT Value + ) +/*++ + +Routine Description: + + This routine sets the ClusterNumber'th 12 bit FAT entry to Value. + +Arguments: + + ClusterNumber - Supplies the FAT entry to set. + Value - Supplies the value to set the FAT entry to. + +Return Value: + + None. + +--*/ +{ + ULONG n; + PUCHAR p; + + p = (PUCHAR) _fat; + + DebugAssert(p); + + n = ClusterNumber*3; + if (n%2) { + p[n/2] = (p[n/2]&0x0F) | ((Value&0x000F)<<4); + p[n/2 + 1] = (Value&0x0FF0)>>4; + } else { + p[n/2] = Value&0x00FF; + p[n/2 + 1] = (p[n/2 + 1]&0xF0) | ((Value&0x0F00)>>8); + } +} + + + + + +USHORT +FAT::QueryFreeClusters( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of free clusters on the disk by + scanning the FAT and counting the number of empty entries. + +Arguments: + + None. + +Return Value: + + The number of free clusters on the disk. + +--*/ +{ + USHORT i; + USHORT r; + + r = 0; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (IsClusterFree(i)) { + r++; + } + } + + return r; +} + + +USHORT +FAT::QueryBadClusters( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of bad clusters on the disk by + scanning the FAT and counting the number of entries marked bad. + +Arguments: + + None. + +Return Value: + + The number of bad clusters on the disk. + +--*/ +{ + USHORT i; + USHORT r; + + r = 0; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (IsClusterBad(i)) { + r++; + } + } + + return r; +} + + +USHORT +FAT::QueryReservedClusters( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of reserved clusters on the disk by + scanning the FAT and counting the number of entries marked reserved. + +Arguments: + + None. + +Return Value: + + The number of reserved clusters on the disk. + +--*/ +{ + USHORT i; + USHORT r; + + r = 0; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (IsClusterReserved(i)) { + r++; + } + } + + return r; +} + +USHORT +FAT::QueryAllocatedClusters( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of allocated clusters on the + disk by scanning the FAT and counting the entries marked allocated. + +Arguments: + + None. + +Return Value: + + The number of allocated clusters on the disk. + +--*/ +{ + USHORT i; + USHORT r; + + r = 0; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (!IsClusterReserved(i) && !IsClusterBad(i) && !IsClusterFree(i)) { + r++; + } + } + + return r; +} + + +UFAT_EXPORT +USHORT +FAT::QueryNthCluster( + IN USHORT StartingCluster, + IN USHORT Index + ) CONST +/*++ + +Routine Description: + + This routine returns the cluster number of the cluster that is in the + 'Index'th position in the cluster chain beginning at 'StartingCluster'. + The clusters in a chain are numbered beginning at zero. + +Arguments: + + StartingCluster - Supplies the first cluster of a cluster chain. + Index - Supplies the number of the cluster in the chain + requested. + +Return Value: + + The cluster number of the 'Index'th cluster in the cluster chain + beginning with cluster 'StartingCluster' or 0. + +--*/ +{ + for (; Index; Index--) { + + if (!IsInRange(StartingCluster)) { + return 0; + } + + StartingCluster = QueryEntry(StartingCluster); + } + + return StartingCluster; +} + + +UFAT_EXPORT +USHORT +FAT::QueryLengthOfChain( + IN USHORT StartingCluster, + OUT PUSHORT LastCluster + ) CONST +/*++ + +Routine Description: + + This routine computes the length of a cluster chain given the number + of its first cluster. + + This routine depends on the chain being valid. In particular, if the + chain contains any cycles then this routine will not finish. The + routine 'ScrubChain' will turn an invalid chain into a valid one. + +Arguments: + + StartingCluster - Supplies the first cluster of a cluster chain. + LastCluster - Returns the number of the last cluster in the chain. + +Return Value: + + The length of the cluster chain beginning with 'StartingCluster'. + +--*/ +{ + USHORT length; + + if (!StartingCluster) { + if (LastCluster) { + *LastCluster = 0; + } + return 0; + } + + for (length = 1; !IsEndOfChain(StartingCluster); length++) { + StartingCluster = QueryEntry(StartingCluster); + } + + if (LastCluster) { + *LastCluster = StartingCluster; + } + + return length; +} + + +USHORT +FAT::QueryLengthOfChain( + IN USHORT StartingCluster, + IN USHORT EndingCluster + ) CONST +/*++ + +Routine Description: + + This routine computes the length of a cluster chain given the number + of its first cluster and the number of its last cluster. To compute + the length of a chain which is terminated by "end of chain", see + the one parameter version of this routine above. If 'EndingCluster' + is not a member of the chain beginning with 'StartingCluster' then + this routine will return 0. + + This routine depends on the chain being valid. + +Arguments: + + StartingCluster - Supplies the first cluster of the cluster chain. + EndingCluster - Supplies the last cluster of the cluster chain. + +Return Value: + + The length of the cluster chain beginning with 'StartingCluster' and + ending with 'EndingCluster' or 0. + +--*/ +{ + USHORT length; + + if (!StartingCluster) { + return 0; + } + + for (length = 1; StartingCluster != EndingCluster && + !IsEndOfChain(StartingCluster); length++) { + StartingCluster = QueryEntry(StartingCluster); + } + + return StartingCluster == EndingCluster ? length : 0; +} + + +USHORT +FAT::QueryPrevious( + IN USHORT Cluster + ) CONST +/*++ + +Routine Description: + + Obtains the previous cluster in a chain, i.e. the cluster that + references the given cluster. + +Arguments: + + Cluster - Supplies the cluster whose predecesor we're looking for. + +Return Value: + + The predecesor of the given cluster. 0 if there is no predecesor. + +--*/ + +{ + USHORT i; + + DebugAssert( Cluster ); + + if ( !IsClusterFree( Cluster ) ) { + for (i = FirstDiskCluster; IsInRange(i); i++) { + if ( QueryEntry(i) == Cluster ) { + return i; + } + } + } + + return 0; +} + + +VOID +FAT::Scrub( + OUT PBOOLEAN ChangesMade + ) +/*++ + +Routine Description: + + This routine goes through all of the FAT entries changing invalid values + to reasonable values for the purposes of CHKDSK. + + Illegal FAT entries are those that are set out of disk range and that + are not magic values. This routine will set all illegal FAT entries to + the "end of chain" magic value. + +Arguments: + + ChangesMade - Returns TRUE if any changes were made to the FAT. + +Return Value: + + None. + +--*/ +{ + USHORT i; + + if (ChangesMade) { + *ChangesMade = FALSE; + } + + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (!IsInRange(QueryEntry(i)) && + !IsClusterFree(i) && + !IsEndOfChain(i) && + !IsClusterBad(i) && + !IsClusterReserved(i)) { + + SetEndOfChain(i); + + if (ChangesMade) { + *ChangesMade = TRUE; + } + } + } +} + + +VOID +FAT::ScrubChain( + IN USHORT StartingCluster, + OUT PBOOLEAN ChangesMade + ) +/*++ + +Routine Description: + + This routine goes through all of the FAT entries in the chain beginning + with cluster 'StartingCluster'. It is expected that all of the entries + in this chain point to valid clusters on the disk. This routine will + mark the first invalid entry, if any, as the final cluster of the chain + thus transforming the invalid chain into a valid one. + +Arguments: + + StartingCluster - Supplies the first cluster of the chain to + scrub. + ChangesMade - Returns TRUE if changes were made to correct + the chain. + +Return Value: + + None. + +--*/ +{ + USHORT clus, next; + + DebugAssert(IsInRange(StartingCluster)); + DebugAssert(ChangesMade); + + *ChangesMade = FALSE; + + clus = StartingCluster; + while (!IsEndOfChain(clus)) { + + next = QueryEntry(clus); + if (!IsInRange(next) || IsClusterFree(next)) { + SetEndOfChain(clus); + *ChangesMade = TRUE; + return; + } + + clus = next; + } +} + + +VOID +FAT::ScrubChain( + IN USHORT StartingCluster, + OUT PBITVECTOR FatBitMap, + OUT PBOOLEAN ChangesMade, + OUT PBOOLEAN CrossLinkDetected, + OUT PUSHORT CrossLinkPreviousCluster + ) +/*++ + +Routine Description: + + This routine goes through all of the FAT entries in the chain beginning + with cluster 'StartingCluster'. It is expected that all of the entries + in this chain point to valid clusters on the disk. This routine will + mark the first invalid entry, if any, as the final cluster of the chain + thus transforming the invalid chain into a valid one. + + This routine will also eliminate any cycles in the cluster chain as well + as detect cross-links. + +Arguments: + + StartingCluster - Supplies the first cluster of the chain to + scrub. + UsedClusters - Supplies a bitvector marking all used + clusters. + ChangesMade - Returns TRUE if changes were made to correct + the chain. + CrossLinkDetected - Returns TRUE if a cluster in the chain was + already claimed in the 'FatBitMap'. + CrossLinkPreviousCluster - Returns the cluster number previous to the + cross linked cluster number or 0 if the + cross linked cluster number was the first + in the chain. + +Return Value: + + None. + +--*/ +{ + USHORT clus, next; + + DebugAssert(IsInRange(StartingCluster)); + DebugAssert(ChangesMade); + DebugAssert(CrossLinkDetected); + DebugAssert(CrossLinkPreviousCluster); + + *ChangesMade = FALSE; + *CrossLinkDetected = FALSE; + + if (FatBitMap->IsBitSet(StartingCluster)) { + *CrossLinkDetected = TRUE; + *CrossLinkPreviousCluster = 0; + return; + } + + clus = StartingCluster; + while (!IsEndOfChain(clus)) { + + FatBitMap->SetBit(clus); + + next = QueryEntry(clus); + if (!IsInRange(next) || IsClusterFree(next)) { + SetEndOfChain(clus); + *ChangesMade = TRUE; + return; + } + + if (FatBitMap->IsBitSet(next)) { + + if (clus == next) { // Cluster points to itself. + *ChangesMade = TRUE; + SetEndOfChain(clus); + return; + } + + while (StartingCluster != clus) { + + if (StartingCluster == next) { // Cluster points to previous. + *ChangesMade = TRUE; + SetEndOfChain(clus); + return; + } + + StartingCluster = QueryEntry(StartingCluster); + } + + // Otherwise it's a cross link, not a cycle. + + *CrossLinkDetected = TRUE; + *CrossLinkPreviousCluster = clus; + return; + } + + clus = next; + } + + FatBitMap->SetBit(clus); +} + +NONVIRTUAL +BOOLEAN +FAT::IsValidChain( + IN USHORT StartingCluster + ) CONST +/*++ + +Routine Description: + + This method determines whether the chain is valid, ie. that it + consists of a chain of valid cluster numbers ending with an end + of chain entry. + +Arguments: + + StartingCluster - Supplies the first cluster of the chain. + +Return Value: + + TRUE if the chain is valid. + +--*/ +{ + USHORT current; + USHORT clusters_in_chain = 0; + + current = StartingCluster; + + for( ;; ) { + + if (!IsInRange(current) || + clusters_in_chain++ > _num_entries ) { + + // Either a bad entry or an infinite loop detected. + // + return FALSE; + } + + if (IsEndOfChain(current)) { + break; + } + + current = QueryEntry(current); + } + + return TRUE; +} + + +UFAT_EXPORT +USHORT +FAT::AllocChain( + IN USHORT Length, + OUT PUSHORT LastCluster + ) +/*++ + +Routine Description: + + This routine attempts to allocate a chain of length 'Length' from the + FAT. If this routine is successful it will return the cluster number + of the beginning of the chain. Upon failure this routine will return + 0 and will make no changes to the FAT. + +Arguments: + + Length - Supplies the length of the chain desired. + LastCluster - Returns the last cluster of the allocated chain. + +Return Value: + + The cluster number of the beginning of the allocated chain or 0. + +--*/ +{ + USHORT i, j; + USHORT start; + USHORT prev; + + if (!Length) { + return 0; + } + + start = 0; + prev = 0; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (IsClusterFree(i)) { + if (!start) { + start = i; + } else { + SetEntry(prev, i); + } + prev = i; + Length--; + if (!Length) { + SetEndOfChain(i); + + if (LastCluster) { + *LastCluster = i; + } + + return start; + } + } + } + + // There is not enough disk space for the chain so free what was taken. + for (i = start; i != prev; ) { + j = QueryEntry(i); + SetClusterFree(i); + i = j; + } + + return 0; +} + + +USHORT +FAT::ReAllocChain( + IN USHORT StartOfChain, + IN USHORT NewLength, + OUT PUSHORT LastCluster + ) +/*++ + +Routine Description: + + This routine insures that the cluster chain beginning at cluster + 'StartOfChain' is of length greater than or equal to 'NewSize'. + If it is not then this routine will attempt to grow the chain by + allocating new clusters. Failure to allocate sufficient clusters + to grow the chain to 'NewSize' clusters will cause this routine to + restore the chain to its original length and state. This routine will + return the current length of the chain : either the old length or the + new length. If an error occurs then 0 will be returned. + +Arguments: + + StartOfChain - Supplies the first cluster of the chain. + NewLength - Supplies the desired new length of the chain. + LastCluster - Returns the last cluster of the chain. + +Return Value: + + The current length of the chain or 0. + +--*/ +{ + USHORT length; + USHORT new_clusters_needed; + USHORT end_of_chain; + USHORT i, j; + USHORT start; + + if (!IsInRange(StartOfChain)) { + return 0; + } + + for (length = 1; !IsEndOfChain(StartOfChain); length++) { + StartOfChain = QueryEntry(StartOfChain); + if (!IsInRange(StartOfChain)) { + return 0; + } + } + + if (length >= NewLength) { + if (LastCluster) { + *LastCluster = StartOfChain; + } + return length; + } + + new_clusters_needed = NewLength - length; + + start = end_of_chain = StartOfChain; + for (i = FirstDiskCluster; IsInRange(i); i++) { + if (IsClusterFree(i)) { + SetEntry(end_of_chain, i); + end_of_chain = i; + new_clusters_needed--; + if (!new_clusters_needed) { + SetEndOfChain(i); + if (LastCluster) { + *LastCluster = i; + } + return NewLength; + } + } + } + + // There is not enough disk space to lengthen the new chain so + // settle for the old length. + + for (i = start; i != end_of_chain; ) { + j = QueryEntry(i); + SetClusterFree(i); + i = j; + } + + SetEndOfChain(start); + + if (LastCluster) { + *LastCluster = start; + } + + return length; +} + + +UFAT_EXPORT +VOID +FAT::FreeChain( + IN USHORT StartOfChain + ) +/*++ + +Routine Description: + + This routine sets free all of the clusters in the cluster chain + beginning with 'StartOfChain'. + +Arguments: + + StartOfChain - Supplies the first cluster of the chain to free. + +Return Value: + + None. + +--*/ +{ + USHORT tmp; + + while (!IsEndOfChain(StartOfChain)) { + tmp = QueryEntry(StartOfChain); + SetClusterFree(StartOfChain); + StartOfChain = tmp; + } + SetClusterFree(StartOfChain); +} diff --git a/private/utils/ufat/src/fatdbsa.cxx b/private/utils/ufat/src/fatdbsa.cxx new file mode 100644 index 000000000..5c6901eee --- /dev/null +++ b/private/utils/ufat/src/fatdbsa.cxx @@ -0,0 +1,1593 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatdbsa.cxx + +Author: + + Matthew Bradburn (mattbr) 30-Sep-93 + +Environment: + + ULIB, User Mode + +--*/ + +#include <pch.cxx> + +#define _NTAPI_ULIB_ + +#include "ulib.hxx" +#include "cluster.hxx" +#include "cmem.hxx" +#include "error.hxx" +#include "fatdent.hxx" +#include "fatdbsa.hxx" +#include "rootdir.hxx" +#include "rtmsg.h" +#include "filedir.hxx" +#include "fat.hxx" +#include "drive.hxx" +#include "cvf.hxx" + +#if !defined(_AUTOCHECK_) && !defined(_SETUP_LOADER_) +#include "timeinfo.hxx" +#endif + +extern UCHAR FatBootCode[512]; + +// Control-C handling is not necessary for autocheck. +#if !defined( _AUTOCHECK_ ) && !defined(_SETUP_LOADER_) + +#include "keyboard.hxx" + +#endif + + +#define CSEC_FAT32MEG 65536 +#define CSEC_FAT16BIT 32680 + +#define MIN_CLUS_BIG 4085 // Minimum clusters for a big FAT. +#define MAX_CLUS_BIG 65525 // Maximum + 1 clusters for big FAT. + +#define sigSUPERSEC1 (UCHAR)0x55 // signature first byte +#define sigSUPERSEC2 (UCHAR)0xAA // signature second byte + +DEFINE_CONSTRUCTOR( FATDB_SA, FAT_SA ); + + +VOID +FATDB_SA::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _fat = NULL; + _dir = NULL; + _cvf_extens = NULL; + _pexbpb = NULL; + _StartDataLbn = 0; + _ClusterCount = 0; + _sysid = SYSID_NONE; +} + + +FATDB_SA::~FATDB_SA( + ) +/*++ + +Routine Description: + + Destructor for FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + +BOOLEAN +FATDB_SA::DosSaInit( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN SECTORCOUNT NumberOfSectors, + IN OUT PMESSAGE Message + ) +{ + if (!SUPERAREA::Initialize(Mem, Drive, NumberOfSectors, Message)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + _sector_sig = (UCHAR *)SECRUN::GetBuf() + 510; + + return TRUE; +} + + +BOOLEAN +FATDB_SA::Initialize( + IN OUT PLOG_IO_DP_DRIVE Drive, + IN OUT PMESSAGE Message, + IN BOOLEAN Formatted + ) +/*++ + +Routine Description: + + This routine initializes the FAT super area to an initial state. It + does so by first reading in the boot sector and verifying it with + the methods of DOS_SUPERAREA. Upon computing the super area's actual size, + the underlying SECRUN will be set to the correct size. + + If the super area does not already exist on disk, then the other Init + function should be called. + +Arguments: + + Drive - Supplies the drive where the super area resides. + Message - Supplies an outlet for messages + Formatted - Supplies a boolean which indicates whether or not + the volume is formatted. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECTORCOUNT sectors; + SECTORCOUNT sectors_in_sa; + CONT_MEM cmem; + SECTORCOUNT reserved; + SECTORCOUNT sec_per_fat; + SECTORCOUNT sec_per_root; + ULONG root_entries; + ULONG sector_size; + BOOLEAN success; + LBN fat_lbn; + ULONG DosRootDirSector; + + Destroy(); + + _sec_per_boot = max(1, BYTES_PER_BOOT_SECTOR/Drive->QuerySectorSize()); + + if (!Formatted) { + return _mem.Initialize() && + DosSaInit(&_mem, Drive, _sec_per_boot, Message); + } + + if (!Drive || + !(sector_size = Drive->QuerySectorSize()) || + !_mem.Initialize() || + !DosSaInit(&_mem, Drive, _sec_per_boot, Message) || + !SECRUN::Read()) { + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + Destroy(); + return FALSE; + } + + CvfUnpackCvfHeader(&_cvf_header, (PPACKED_CVF_HEADER)SECRUN::GetBuf()); + + if (!VerifyBootSector() || 0 == _cvf_header.Bpb.Fats) { + Destroy(); + return FALSE; + } + + reserved = _cvf_header.Bpb.ReservedSectors; + sec_per_fat = _cvf_header.Bpb.SectorsPerFat; + root_entries = _cvf_header.Bpb.RootEntries; + sec_per_root = (root_entries*BytesPerDirent - 1)/sector_size + 1; + + _StartDataLbn = _cvf_header.DosBootSectorLbn + _cvf_header.CvfHeapOffset; + + sectors = (0 == _cvf_header.Bpb.LargeSectors) ? + 0 : _cvf_header.Bpb.LargeSectors /* - _StartDataLbn */; + + _ClusterCount = (USHORT)(sectors/QuerySectorsPerCluster() + + FirstDiskCluster); + + _ft = (_ClusterCount >= 4087) ? LARGE : SMALL; + + if (_ft == SMALL) { + _sysid = SYSID_FAT12BIT; + } else if (QueryVirtualSectors() < CSEC_FAT32MEG) { + _sysid = SYSID_FAT16BIT; + } else { + _sysid = SYSID_FAT32MEG; + } + + // + // Note here that the CvfHeapOffset is measured from the DosBootSector, + // not from the beginning of the CVF. + // + + sectors_in_sa = _cvf_header.DosBootSectorLbn + _cvf_header.CvfHeapOffset; + + + if (!_mem.Initialize() || + !DosSaInit(&_mem, Drive, sectors_in_sa, Message)) { + Destroy(); + return FALSE; + } + + // + // Set up the FAT extensions + // + + if (!cmem.Initialize((PCHAR)SECRUN::GetBuf() + + (_cvf_header.CvfFatExtensionsLbnMinus1 + 1) * sector_size, + (_cvf_header.DosBootSectorLbn - + (_cvf_header.CvfFatExtensionsLbnMinus1 + 1)) * sector_size)) { + Destroy(); + return FALSE; + } + + DELETE(_cvf_extens); + if (NULL == (_cvf_extens = NEW CVF_FAT_EXTENS)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + if (!_cvf_extens->Initialize(&cmem, _drive, + _cvf_header.CvfFatExtensionsLbnMinus1 + 1, _ClusterCount, + _cvf_header.CvfFatFirstDataEntry )) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + // + // Set up the pointer to the second bpb, the DOS_BPB. + // + + _pexbpb = (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK) + ((PCHAR)SECRUN::GetBuf() + _cvf_header.DosBootSectorLbn * sector_size); + + // + // Set up the fat. + // + + fat_lbn = _cvf_header.DosBootSectorLbn + _cvf_header.Bpb.ReservedSectors; + + if (!cmem.Initialize((PCHAR)SECRUN::GetBuf() + fat_lbn*sector_size, + QuerySectorsPerFat()*sector_size)) { + Destroy(); + return FALSE; + } + + if (!(_fat = NEW FAT)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + if (!_fat->Initialize(&cmem, _drive, fat_lbn, _ClusterCount, + QuerySectorsPerFat())) { + Destroy(); + return FALSE; + } + + // + // Set up root directory. + // + + if (NULL == (_dir = NEW ROOTDIR)) { + Destroy(); + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + DosRootDirSector = _cvf_header.DosBootSectorLbn + + _cvf_header.DosRootDirectoryOffset; + + success = cmem.Initialize((PCHAR)SECRUN::GetBuf() + + DosRootDirSector * sector_size, + sec_per_root * sector_size); + if (!success) { + Destroy(); + return FALSE; + } + + success = _dir->Initialize(&cmem, Drive, DosRootDirSector, root_entries); + if (!success) { + Destroy(); + return FALSE; + } + + // + // Set up the bitmap for the sector heap. + // + + _sector_heap_bitmap.Initialize(_cvf_header.Bpb.LargeSectors - _StartDataLbn); + _sector_heap_init = FALSE; + + return TRUE; +} + + +BOOLEAN +FATDB_SA::Create( + IN PCNUMBER_SET BadSectors, + IN OUT PMESSAGE Message, + IN PCWSTRING Label, + IN ULONG ClusterSize, + IN ULONG VirtualSize + ) +/*++ + +Routine Description: + + This routine initializes the FAT file system. + +Arguments: + + BadSectors - Supplies a list of the bad sectors on the volume. + Message - Supplies an outlet for messages. + Label - Supplies an optional label. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _SETUP_LOADER_ ) + + return FALSE; + +#else + + USHORT sector_size; + SECTORCOUNT sec_per_root; + CONT_MEM cmem; + HMEM hmem; + SECRUN secrun; + SECRUN small_secrun; + PUSHORT p; + USHORT cluster_count; + ULONG cluster_size; + DSTRING label; + USHORT free_count; + USHORT bad_count; + PPACKED_CVF_HEADER SourceBootSector, TargetBootSector; + ULONG BootCodeOffset; + BOOLEAN fReInit; + SECTORCOUNT sectors; + SECTORCOUNT sectors_in_sa; + LBN fat_lbn; + SECRUN last_sector; + HMEM last_sector_mem; + ULONG DosRootDirSector; + BOOLEAN success; + + if (!_drive || + !(sector_size = (USHORT) _drive->QuerySectorSize()) || + (_sysid = ComputeSystemId()) == SYSID_NONE) { + return FALSE; + } + + if (VirtualSize != 0) { + _cvf_header.Bpb.LargeSectors = VirtualSize; + } else if (_cvf_header.Bpb.LargeSectors == 0) { + if (_drive->QuerySectors().GetHighPart() != 0) { + // This should be checked before calling this procedure. + DbgAbort("Number of sectors exceeds 32 bits"); + return FALSE; + } + _cvf_header.Bpb.LargeSectors = _drive->QuerySectors().GetLowPart(); + } + + sec_per_root = (_cvf_header.Bpb.RootEntries*BytesPerDirent - 1)/ + sector_size + 1; + + _StartDataLbn = _cvf_header.DosBootSectorLbn + _cvf_header.CvfHeapOffset; + + // + // If the _ClusterCount is 0, we need to re-initialize the fat + // and fat extensions with the correct size. + // + + fReInit = (0 == _ClusterCount); + + sectors = _cvf_header.Bpb.LargeSectors - _StartDataLbn; + + _ClusterCount = (USHORT)(sectors/QuerySectorsPerCluster() + + FirstDiskCluster); + + if (fReInit) { + if (!_cvf_extens->Initialize(&cmem, _drive, + _cvf_header.CvfFatExtensionsLbnMinus1 + 1, _ClusterCount, + _cvf_header.CvfFatFirstDataEntry)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + fat_lbn = _cvf_header.DosBootSectorLbn + _cvf_header.Bpb.ReservedSectors; + + if (!_fat->Initialize(&cmem, _drive, fat_lbn, _ClusterCount, + QuerySectorsPerFat())) { + Destroy(); + return FALSE; + } + } + + // + // Note here that the CvfHeapOffset is measured from the DosBootSector, + // not the beginning of the CVF. + // + + sectors_in_sa = _cvf_header.DosBootSectorLbn + _cvf_header.CvfHeapOffset; + + if (!_mem.Initialize() || + !DosSaInit(&_mem, _drive, sectors_in_sa, Message)) { + return FALSE; + } + + // + // Set up the FAT extensions + // + + if (!cmem.Initialize((PCHAR)SECRUN::GetBuf() + + (_cvf_header.CvfFatExtensionsLbnMinus1 + 1) * sector_size, + (_cvf_header.DosBootSectorLbn - + (_cvf_header.CvfFatExtensionsLbnMinus1 + 1)) * sector_size)) { + Destroy(); + return FALSE; + } + + DELETE(_cvf_extens); + if (NULL == (_cvf_extens = NEW CVF_FAT_EXTENS)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + if (!_cvf_extens->Initialize(&cmem, _drive, + _cvf_header.CvfFatExtensionsLbnMinus1 + 1, _ClusterCount, + _cvf_header.CvfFatFirstDataEntry)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + // + // Set up the pointer to the second bpb, the DOS_BPB. + // + + _pexbpb = (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK) + ((PCHAR)SECRUN::GetBuf() + _cvf_header.DosBootSectorLbn * sector_size); + + // + // Set up the fat. + // + + fat_lbn = _cvf_header.DosBootSectorLbn + _cvf_header.Bpb.ReservedSectors; + + if (!cmem.Initialize((PCHAR)SECRUN::GetBuf() + fat_lbn*sector_size, + QuerySectorsPerFat()*sector_size)) { + Destroy(); + return FALSE; + } + + if (!(_fat = NEW FAT)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + Destroy(); + return FALSE; + } + + if (!_fat->Initialize(&cmem, _drive, fat_lbn, _ClusterCount, + QuerySectorsPerFat())) { + Destroy(); + return FALSE; + } + + // + // Set up root directory. + // + + if (NULL == (_dir = NEW ROOTDIR)) { + Destroy(); + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + DosRootDirSector = _cvf_header.DosBootSectorLbn + + _cvf_header.DosRootDirectoryOffset; + + success = cmem.Initialize((PCHAR)SECRUN::GetBuf() + + DosRootDirSector * sector_size, + sec_per_root * sector_size); + if (!success) { + Destroy(); + return FALSE; + } + + success = _dir->Initialize(&cmem, _drive, DosRootDirSector, _cvf_header.Bpb.RootEntries); + if (!success) { + Destroy(); + return FALSE; + } + + + + // + // Zero fill the super area, excepting the CVF_HEADER, which we + // don't touch. + // + + memset((PUCHAR)_mem.GetBuf() + sector_size, + 0, _mem.QuerySize() - sector_size); + + if (!SetSystemId()) { + Message->Set(MSG_WRITE_PARTITION_TABLE); + Message->Display(""); + return FALSE; + } + + _cvf_extens->Create(); + + _fat->SetEarlyEntries(_cvf_header.Bpb.Media); + + Message->Set(MSG_FORMAT_COMPLETE); + Message->Display(""); + + if (_drive->QueryMediaType() != F5_160_512 && + _drive->QueryMediaType() != F5_320_512) { + + if (Label) { + if (!label.Initialize(Label)) { + return FALSE; + } + } else { + switch (_drive->QueryRecommendedMediaType()) { + case F5_360_512: + case F5_320_512: + case F5_180_512: + case F5_160_512: + // These disk drives are lame and can't + // take the spin down without a verify + // so don't prompt for the label. + // This will avoid FORMAT failing. + + label.Initialize(); + break; + + default: + Message->Set(MSG_VOLUME_LABEL_PROMPT); + Message->Display(""); + Message->QueryStringInput(&label); + break; + + } + } + + while (!SetLabel(&label)) { + + Message->Set(MSG_INVALID_LABEL_CHARACTERS); + Message->Display(""); + + Message->Set(MSG_VOLUME_LABEL_PROMPT); + Message->Display(""); + Message->QueryStringInput(&label); + } + } + + // + // Copy the boot code into the secrun's buffer. + // This is complicated by the fact that DOS_SA::Write + // packs the data from the unpacked boot sector into + // the packed boot sector, so we have to set the + // first few fields in the unpacked version. + // + SourceBootSector = (PPACKED_CVF_HEADER)FatBootCode; + + CopyUchar2(&_cvf_header.JmpOffset, + SourceBootSector->JmpOffset); + CopyUchar1(&_cvf_header.Jump, + SourceBootSector->Jump); + + // + // Copy the remainder of the boot code directly into + // the secrun. + // + TargetBootSector = (PPACKED_CVF_HEADER)SECRUN::GetBuf(); + + BootCodeOffset = FIELD_OFFSET( PACKED_CVF_HEADER, StartBootCode ); + + memcpy( (PUCHAR)TargetBootSector + BootCodeOffset, + (PUCHAR)SourceBootSector + BootCodeOffset, + sizeof( FatBootCode ) - BootCodeOffset ); + + // + // Write the second Double Space signature in the last + // sector of the CVF. + // + if( !last_sector_mem.Initialize() || + !last_sector.Initialize( &last_sector_mem, + _drive, + _drive->QuerySectors() - 1, + 1 ) ) { + + Message->Set(MSG_UNUSABLE_DISK); + Message->Display(""); + return FALSE; + } + + memset( last_sector.GetBuf(), 0, _drive->QuerySectorSize() ); + memcpy( last_sector.GetBuf(), SecondDbSignature, DbSignatureLength ); + + if( !last_sector.Write() ) { + + Message->Set(MSG_UNUSABLE_DISK); + Message->Display(""); + return FALSE; + } + + + // + // Finally, write the changes to disk. + // + if (!Write(Message)) { + if (_drive->QueryLastNtStatus() == STATUS_MEDIA_WRITE_PROTECTED) { + Message->Set(MSG_FMT_WRITE_PROTECTED_MEDIA); + } else { + Message->Set(MSG_UNUSABLE_DISK); + } + Message->Display(""); + return FALSE; + } + + // + // Print an informative report. + // + cluster_count = QueryClusterCount() - FirstDiskCluster; + cluster_size = sector_size*QuerySectorsPerCluster(); + + Message->Set(MSG_TOTAL_DISK_SPACE); + Message->Display("%9u", cluster_count*cluster_size); + + if (bad_count = _fat->QueryBadClusters()) { + Message->Set(MSG_BAD_SECTORS); + Message->Display("%9u", bad_count*cluster_size); + } + + free_count = _fat->QueryFreeClusters(); + + Message->Set(MSG_AVAILABLE_DISK_SPACE); + Message->Display("%9u", free_count*cluster_size); + + Message->Set(MSG_ALLOCATION_UNIT_SIZE); + Message->Display("%9u", cluster_size); + + Message->Set(MSG_AVAILABLE_ALLOCATION_UNITS); + Message->Display("%9u", free_count); + + if (QueryVolId()) { + Message->Set(MSG_BLANK_LINE); + Message->Display(); + p = (PUSHORT)&_dos_exbpb.SerialNumber; + Message->Set(MSG_VOLUME_SERIAL_NUMBER); + Message->Display("%04X%04X", p[1], p[0]); + } + + return TRUE; + +#endif +} + + +BOOLEAN +FATDB_SA::Read( + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine reads the super area. It will succeed if it can + read the boot sector, the root directory, and at least one of + the FATs. + + If the position of the internal FAT has not yet been determined, + this routine will attempt to map it to a readable FAT on the disk. + +Arguments: + + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECRUN secrun; + CONT_MEM cmem; + + if (!SECRUN::Read()) { + + // Check to see if super area was allocated as formatted. + if (QueryLength() <= _sec_per_boot) { + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + // Check the boot sector. + if (!secrun.Initialize(&_mem, _drive, 0, _sec_per_boot) || + !secrun.Read()) { + CvfUnpackCvfHeader(&_cvf_header, + (PPACKED_CVF_HEADER)SECRUN::GetBuf()); + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + // Check the fat extensions. + + if (!_cvf_extens->Read()) { + Message->Set(MSG_CANT_READ_FAT_EXTENS); + Message->Display(""); + return FALSE; + } + + // Check the root directory. + if (!_dir || !_dir->Read()) { + Message->Set(MSG_BAD_DIR_READ); + Message->Display(""); + return FALSE; + } + + // Check the fat. + + if (!_fat->Read()) { + Message->Set(MSG_DISK_ERROR_READING_FAT); + Message->Display("%d", 1 + + (_fat->QueryStartLbn() - _cvf_header.Bpb.ReservedSectors)/ + _cvf_header.Bpb.SectorsPerFat); + return FALSE; + } + + } else { + + CvfUnpackCvfHeader(&_cvf_header, + (PPACKED_CVF_HEADER)SECRUN::GetBuf()); + + UnpackExtendedBios(&_dos_exbpb, _pexbpb); + } + + return TRUE; +} + + +BOOLEAN +FATDB_SA::Write( + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine writes the super area. It will succeed if it can + write the boot sector, the root directory, and at least one of + the FATs. + + This routine will duplicate the working FAT to all other FATs + in the super area. + +Arguments: + + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECRUN secrun; + + SetExtendedBpb(); + + // + // Pack the dos_bpb and CVF Header into the secrun at the + // right place. Put the two interesting signatures at the + // end of the DOS Boot Sector and the beginning of the next + // sector. + // + PackExtendedBios(&_dos_exbpb, _pexbpb); + *(((PUCHAR)_pexbpb) + 510) = 0x55; + *(((PUCHAR)_pexbpb) + 511) = 0xAA; + memcpy( ((PUCHAR)_pexbpb) + 512, FirstDbSignature, DbSignatureLength ); + + CvfPackCvfHeader((PPACKED_CVF_HEADER)SECRUN::GetBuf(), + &_cvf_header); + + + if (!SECRUN::Write()) { + + if (!_fat || !_dir) { + Message->Set(MSG_CANT_WRITE_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + if (!secrun.Initialize(&_mem, _drive, 0, _sec_per_boot) || + !secrun.Write()) { + Message->Set(MSG_CANT_WRITE_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + if (!_dir->Write()) { + Message->Set(MSG_CANT_WRITE_ROOT_DIR); + Message->Display(""); + return FALSE; + } + + if (!_cvf_extens->Write()) { + Message->Set(MSG_CANT_WRITE_FAT_EXTENS); + Message->Display(""); + return FALSE; + } + + if (!_fat->Write()) { + Message->Set(MSG_BAD_FAT_WRITE); + Message->Display(""); + return FALSE; + } else { + Message->Set(MSG_SOME_FATS_UNWRITABLE); + Message->Display(""); + } + } + + return TRUE; +} + + +SECTORCOUNT +FATDB_SA::QueryFreeSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of unused sectors on disk. + +Arguments: + + None. + +Return Value: + + The number of free sectors on disk. + +--*/ +{ + if (!_fat) { + perrstk->push(ERR_NOT_READ, QueryClassId()); + return 0; + } + + return _fat->QueryFreeClusters()*QuerySectorsPerCluster(); +} + + +FATTYPE +FATDB_SA::QueryFatType( + ) CONST +/*++ + +Routine Description: + + This routine computes the FATTYPE of the FAT for this volume. + +Arguments: + + None. + +Return Value: + + The FATTYPE for the FAT. + +--*/ +{ + return _ft; +} + + +VOID +FATDB_SA::Destroy( + ) +/*++ + +Routine Description: + + This routine cleans up the local data in the fat super area. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DELETE(_fat); + DELETE(_dir); + + _StartDataLbn = 0; + _ClusterCount = 0; + _sysid = SYSID_NONE; +} + +#if !defined(_SETUP_LOADER_) + +USHORT +FATDB_SA::ComputeRootEntries( + ) CONST +/*++ + +Routine Description: + + This routine uses the size of the disk and a standard table in + order to compute the required number of root directory entries. + +Arguments: + + None. + +Return Value: + + The required number of root directory entries. + +--*/ +{ + switch (_drive->QueryMediaType()) { + + case F3_720_512: + case F5_360_512: + case F5_320_512: + case F5_320_1024: + case F5_180_512: + case F5_160_512: + return 112; + + case F5_1Pt2_512: + case F3_1Pt44_512: + return 224; + + case F3_2Pt88_512: + case F3_20Pt8_512: + return 240; + } + + return 512; +} + + +USHORT +FATDB_SA::ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ) +/*++ + +Routine Description: + + This routine computes the number of sectors per cluster required + based on the actual number of sectors. + +Arguments: + + Sectors - Supplies the total number of sectors on the disk. + FatType - Supplies the type of FAT. + MediaType - Supplies the type of the media. + +Return Value: + + The required number of sectors per cluster. + +--*/ +{ + USHORT sec_per_clus; + SECTORCOUNT threshold; + + if (FatType == SMALL) { + threshold = MIN_CLUS_BIG; + sec_per_clus = 1; + } else { + threshold = MAX_CLUS_BIG; + sec_per_clus = 1; + } + + while (Sectors >= threshold) { + sec_per_clus *= 2; + threshold *= 2; + } + + switch (MediaType) { + + case F5_320_512: + case F5_360_512: + case F3_720_512: + case F3_2Pt88_512: + sec_per_clus = 2; + break; + + case F3_20Pt8_512: + sec_per_clus = 4; + break; + + default: + break; + + } + + return sec_per_clus; +} + +#endif // _SETUP_LOADER_ + +ULONG +FATDB_SA::SecPerBoot() +{ + return _sec_per_boot; +} + +INLINE +BOOLEAN +FATDB_SA::SetOemData( + ) +/*++ + +Routine Description: + + This routine sets the OEM data in both the CVF MDBPB and + the DOS Sector 0 Extended BPB. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + memcpy( (void*)_cvf_header.Oem, (void*)OEMDBTEXT, OEMTEXTLENGTH);\ + memcpy( (void*)_dos_exbpb.OemData, (void*)OEMDBTEXT, OEMTEXTLENGTH ); + return TRUE; +} + +BOOLEAN +FATDB_SA::SetSignature( + ) +/*++ + +Routine Description: + + This routine sets the sector zero signature in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + if (!_sector_sig) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return FALSE; + } + + *_sector_sig = sigSUPERSEC1; + *(_sector_sig + 1) = sigSUPERSEC2; + + + return TRUE; +} + +BOOLEAN +FATDB_SA::SetBootCode( + ) +/*++ + +Routine Description: + + This routine sets the boot code in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + _cvf_header.Jump = 0xEB; + _cvf_header.JmpOffset = 0x903C; + SetBootSignature(); + return TRUE; +} + +BOOLEAN +FATDB_SA::VerifyBootSector( + ) +/*++ + +Routine Description: + + This routine checks key parts of sector 0 to insure that the data + being examined is indeed a zero sector. + +Arguments: + + None. + +Return Value: + + FALSE - Invalid sector zero. + TRUE - Valid sector zero. + +--*/ +{ + PUCHAR p; + +// We don't check for 55 AA anymore because we have reason to +// believe that there are versions of FORMAT out there that +// don't put it down. + +#if 0 + if (!IsFormatted()) { + return FALSE; + } +#endif + + p = (PUCHAR) GetBuf(); + + return p[0] == 0xE9 || (p[0] == 0xEB && p[2] == 0x90); +} + +BOOLEAN +FATDB_SA::SetPhysicalDriveType( + IN PHYSTYPE PhysType + ) +/*++ + +Routine Description: + + This routine sets the physical drive type in the super area. + +Arguments: + + PhysType -- Supplies the physical drive type. + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + _dos_exbpb.PhysicalDrive = (UCHAR)PhysType; + return TRUE; +} + +BOOLEAN +FATDB_SA::SetBpb( + ) +{ + // BUGBUG billmc -- do we need this? + // + DbgPrintf( "UFAT: Unsupported function FATDB_SA::SetBpb called.\n" ); + return FALSE; +} + +BOOLEAN +FATDB_SA::SetExtendedBpb( + ) +/*++ + +Routine Description: + + This routine sets fields in the extended dos bpb. + +Arguments: + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + // + // Copy the bpb to the dos_bpb, then set the fields that are + // different. + // + _dos_exbpb.IntelNearJumpCommand = _cvf_header.Jump; + _dos_exbpb.BootStrapJumpOffset = _cvf_header.JmpOffset; + + memcpy(&_dos_exbpb.Bpb, &_cvf_header.Bpb, sizeof(_cvf_header.Bpb)); + + _dos_exbpb.Bpb.Fats = 2; + _dos_exbpb.Bpb.LargeSectors += _cvf_header.Bpb.SectorsPerFat; + + SetVolId(ComputeVolId()); + + if (_ft == SMALL) { + memcpy(_dos_exbpb.SystemIdText, "FAT12 ", cSYSID); + } else { + memcpy(_dos_exbpb.SystemIdText, "FAT16 ", cSYSID); + } + + memcpy(_dos_exbpb.Label, "DBLSPACE ", cLABEL); + + return + SetPhysicalDriveType(_drive->IsRemovable() ? + PHYS_REMOVABLE : PHYS_FIXED) && + SetOemData() && + SetBootSignature() && + SetSignature(); +} + +BOOLEAN +FATDB_SA::RecoverChain( + IN OUT PUSHORT StartingCluster, + OUT PBOOLEAN ChangesMade, + IN USHORT EndingCluster, + IN BOOLEAN Replace + ) +{ + // This is here to support FAT_SA::RecoverFile. + return TRUE; +} + +BOOLEAN +FATDB_SA::IsClusterCompressed( + IN ULONG Cluster + ) CONST +{ + return _cvf_extens->IsClusterCompressed(Cluster); +} + +ULONG +FATDB_SA::QuerySectorFromCluster( + IN ULONG Cluster, + IN OUT PUCHAR NumSectors + ) +{ + return _cvf_extens->QuerySectorFromCluster(Cluster, NumSectors); +} + +VOID +FATDB_SA::SetClusterCompressed( + IN ULONG Cluster, + IN BOOLEAN bCompressed + ) +{ + _cvf_extens->SetClusterCompressed(Cluster, bCompressed); +} + +UCHAR +FATDB_SA::QuerySectorsRequiredForPlainData( + IN ULONG Cluster + ) +{ + return _cvf_extens->QuerySectorsRequiredForPlainData(Cluster); +} + +BOOLEAN +FATDB_SA::VerifyFatExtensions( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN PBOOLEAN pfNeedMsg + ) +/*++ + +Routine Description: + + This routine examines the fat extensions, changing entries if + necessary to ensure that they match the fat and that they are + valid. + +Arguments: + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ + ULONG cluster; + + for (cluster = FirstDiskCluster; cluster < _ClusterCount; ++cluster) { + + if (!_cvf_extens->IsClusterInUse(cluster) && + (_fat->IsClusterFree(cluster) || _fat->IsClusterBad(cluster) || + _fat->IsClusterReserved(cluster))) { + continue; + } + + if ((_fat->IsInRange(_fat->QueryEntry(cluster)) || + _fat->IsEndOfChain(cluster)) && + !_cvf_extens->IsClusterInUse(cluster)) { + + // + // The fat thinks this entry is in use, but the fat extensions + // disagree. Clear the fat entry. + // + + dofmsg(Message, pfNeedMsg); + Message->Set(MSG_DBLSPACE_FAT_EXTENS_MISMATCH); + Message->Display("%d", cluster); + + _fat->SetClusterFree(cluster); + continue; + } + + if (_cvf_extens->IsClusterInUse(cluster) && + (_fat->IsClusterBad(cluster) || _fat->IsClusterReserved(cluster) + || _fat->IsClusterFree(cluster))) { + + // + // The cvf_extens think this entry is in use, but the fat + // disagrees. Clear the cvf extens entry. + // + + dofmsg(Message, pfNeedMsg); + Message->Set(MSG_DBLSPACE_FAT_EXTENS_MISMATCH); + Message->Display("%d", cluster); + + _cvf_extens->SetClusterInUse(cluster, FALSE); + _cvf_extens->SetSectorForCluster(cluster, 1, 1); + _cvf_extens->SetClusterCompressed(cluster, FALSE); + _cvf_extens->SetSectorsRequiredForPlainData(cluster, 1); + continue; + } + + // + // Ensure that the fat extensions entry is valid. + // + + UCHAR nsec; + ULONG first_sector; + + first_sector = _cvf_extens->QuerySectorFromCluster(cluster, &nsec); + + if (first_sector > _cvf_header.Bpb.LargeSectors - _StartDataLbn || + nsec == 0 || nsec > QuerySectorsPerCluster() || + _cvf_extens->QuerySectorsRequiredForPlainData(cluster) > + QuerySectorsPerCluster()) { + + // This entry is bogus. + + dofmsg(Message, pfNeedMsg); + Message->Set(MSG_DBLSPACE_FAT_EXTENS_INVALID); + Message->Display("%d", cluster); + + _fat->SetClusterFree(cluster); + _cvf_extens->SetClusterInUse(cluster, FALSE); + _cvf_extens->SetSectorForCluster(cluster, 1, 1); + _cvf_extens->SetClusterCompressed(cluster, FALSE); + _cvf_extens->SetSectorsRequiredForPlainData(cluster, 1); + } + } + + return TRUE; +} + +BOOLEAN +FATDB_SA::CheckSectorHeapAllocation( + IN FIX_LEVEL FixLevel, + IN PMESSAGE Message, + IN OUT PBOOLEAN NeedMsg + ) +{ + ULONG cluster; + ULONG first_sec; + UCHAR nsec; + + DbgAssert(!_sector_heap_init); + + for (cluster = FirstDiskCluster; cluster < _ClusterCount; ++cluster) { + if (!_cvf_extens->IsClusterInUse(cluster)) { + continue; + } + + // + // the sector numbers in the fat extensions is from the beginning of + // the cvf, but the bitmap is from the beginning of the sector heap. + // + + first_sec = _cvf_extens->QuerySectorFromCluster(cluster, &nsec) + - _StartDataLbn; + + if (first_sec > _cvf_header.Bpb.LargeSectors - _StartDataLbn) { + // + // Sector number out of range. Clear this entry. + // +DbgPrintf("clus 0x%x, lbn 0x%x\n", cluster, _cvf_extens->QuerySectorFromCluster(cluster)); + + dofmsg(Message, NeedMsg); + Message->Set(MSG_DBLSPACE_SECTOR_RANGE); + Message->Display(""); + + _fat->SetClusterFree(cluster); + _cvf_extens->SetClusterInUse(cluster, FALSE); + _cvf_extens->SetSectorForCluster(cluster, 1, 1); + _cvf_extens->SetClusterCompressed(cluster, FALSE); + _cvf_extens->SetSectorsRequiredForPlainData(cluster, 1); + + continue; + + } + + for (ULONG i = first_sec; i < first_sec + nsec; ++i) { + if (_sector_heap_bitmap.IsBitSet(i)) { + // + // This sector is allocated to another cluster. Clear this + // entry. + // + + dofmsg(Message, NeedMsg); + Message->Set(MSG_DBLSPACE_SECTOR_DUP_ALLOC); + Message->Display(""); + + _fat->SetClusterFree(cluster); + _cvf_extens->SetClusterInUse(cluster, FALSE); + _cvf_extens->SetSectorForCluster(cluster, 1, 1); + _cvf_extens->SetClusterCompressed(cluster, FALSE); + _cvf_extens->SetSectorsRequiredForPlainData(cluster, 1); + + break; + } + } + + if (_cvf_extens->IsClusterInUse(cluster)) { + _sector_heap_bitmap.SetBit(first_sec, nsec); + } + } + + _sector_heap_init = TRUE; + return TRUE; +} + +BOOLEAN +FATDB_SA::AllocateClusterData( + IN ULONG Cluster, + IN UCHAR NumSectors, + IN BOOLEAN bCompressed, + IN UCHAR PlainSize + ) +{ + DbgAssert(NumSectors <= PlainSize); + DbgAssert(bCompressed || NumSectors == PlainSize); + DbgAssert(_sector_heap_init); + + for (ULONG i = 0; i < _sector_heap_bitmap.QuerySize() - NumSectors; ++i) { + + for (int j = 0; j < NumSectors; ++j) { + if (_sector_heap_bitmap.IsBitSet(i + j)) + break; + } + + if (j == NumSectors) { + + // We have found a suitable free space. + + _sector_heap_bitmap.SetBit(i, NumSectors); + _cvf_extens->SetSectorForCluster(Cluster, i + _StartDataLbn, + NumSectors); + _cvf_extens->SetClusterCompressed(Cluster, bCompressed); + _cvf_extens->SetSectorsRequiredForPlainData(Cluster, PlainSize); + _cvf_extens->SetClusterInUse(Cluster, TRUE); + + return TRUE; + } + } + + // error: no space + + return FALSE; +} + +BOOLEAN +FATDB_SA::FreeClusterData( + IN ULONG Cluster + ) +{ + ULONG first_sector; + UCHAR nsec; + + DbgAssert(_sector_heap_init); + + first_sector = _cvf_extens->QuerySectorFromCluster(Cluster, &nsec) + - _StartDataLbn; + + _cvf_extens->SetSectorForCluster(Cluster, 1, 1); + _cvf_extens->SetClusterInUse(Cluster, FALSE); + + DbgAssert(_sector_heap_bitmap.IsBitSet(first_sector)); + DbgAssert(_sector_heap_bitmap.IsBitSet(first_sector + nsec - 1)); + + _sector_heap_bitmap.ResetBit(first_sector, nsec); + + return TRUE; +} + +BOOLEAN +FATDB_SA::SetCvfSectorCount( + IN ULONG NewSectorCount + ) +{ + ULONG r; + + _cvf_header.Bpb.LargeSectors = NewSectorCount; + + r = _sector_heap_bitmap.SetSize(NewSectorCount - _StartDataLbn); + if (0 == r) { + return FALSE; + } + + if (!Write(NULL)) { + return FALSE; + } + + return TRUE; +} diff --git a/private/utils/ufat/src/fatdbvol.cxx b/private/utils/ufat/src/fatdbvol.cxx new file mode 100644 index 000000000..65b9d5f70 --- /dev/null +++ b/private/utils/ufat/src/fatdbvol.cxx @@ -0,0 +1,139 @@ +#include <pch.cxx> + +#define _NTAPI_ULIB_ + +#include "ulib.hxx" +#include "error.hxx" +#include "fatdbvol.hxx" + +#include "message.hxx" +#include "rtmsg.h" +#include "wstring.hxx" + + +DEFINE_CONSTRUCTOR( FATDB_VOL, VOL_LIODPDRV ); + +VOID +FATDB_VOL::Construct ( + ) + +/*++ + +Routine Description: + + Constructor for FATDB_VOL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // unreferenced parameters + (void)(this); +} + +VOID +FATDB_VOL::Destroy( + ) +{ + (void)(this); +} + +FATDB_VOL::~FATDB_VOL( + ) +/*++ + +Routine Description: + + Destructor for FATDB_VOL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + +BOOLEAN +FATDB_VOL::Initialize( + IN PCWSTRING NtDriveName, + IN PCWSTRING HostFileName, + IN OUT PMESSAGE Message, + IN BOOLEAN ExclusiveWrite + ) +/*++ + +Routine Description: + + This routine initializes a FATDB_VOL object. + +Arguments: + + NtDriveName - Supplies the drive path for the volume. + HostFileName - Supplies the name of the file which contains + this volume. + Message - Supplies an outlet for messages. + ExclusiveWrite - Supplies whether or not the drive should be + opened for exclusive write. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + MESSAGE msg; + + Destroy(); + + if (!VOL_LIODPDRV::Initialize(NtDriveName, + HostFileName, + &_fatdbsa, + Message, + ExclusiveWrite)) { + Destroy(); + return FALSE; + } + + + if (!Message) { + Message = &msg; + } + + if (!_fatdbsa.Initialize(this, &msg, TRUE)) { + Destroy(); + return FALSE; + } + + if (!_fatdbsa.Read(Message)) { + Destroy(); + return FALSE; + } + + return TRUE; +} + +PVOL_LIODPDRV +FATDB_VOL::QueryDupVolume( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN ExclusiveWrite, + IN BOOLEAN FormatMedia, + IN MEDIA_TYPE MediaType + ) CONST +{ + DbgPrintf( "UFAT: Unsupported function FATDB_VOL::QueryDupVolume called.\n" ); + return FALSE; +} diff --git a/private/utils/ufat/src/fatdent.cxx b/private/utils/ufat/src/fatdent.cxx new file mode 100644 index 000000000..25718762c --- /dev/null +++ b/private/utils/ufat/src/fatdent.cxx @@ -0,0 +1,1125 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" +#include "ifssys.hxx" +#include "wstring.hxx" + +// TimeInfo is full of windows stuff. +#if !defined( _AUTOCHECK_ ) && !defined( _SETUP_LOADER_ ) + +#include "timeinfo.hxx" + +#endif + + +DEFINE_EXPORTED_CONSTRUCTOR( FAT_DIRENT, OBJECT, UFAT_EXPORT ); + +VOID +FAT_DIRENT::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FAT_DIRENT. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _dirent = NULL; +} + + +UFAT_EXPORT +FAT_DIRENT::~FAT_DIRENT( + ) +/*++ + +Routine Description: + + Destructor for FAT_DIRENT. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::Initialize( + IN OUT PVOID Dirent + ) +/*++ + +Routine Description: + + This routine initializes the object to use the directory entry + pointed to by Dirent. + +Arguments: + + Dirent - Supplies the directory entry. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + _dirent = (PUCHAR) Dirent; + return _dirent ? TRUE : FALSE; +} + + +UFAT_EXPORT +VOID +FAT_DIRENT::QueryName( + OUT PWSTRING Name + ) CONST +/*++ + +Routine Description: + + This routine copies the directory entries name to 'Name' in an + appropriate format. + + Directories and files will be returned in compressed 8.3 format. + Labels will be returned in compressed 11 character format. + +Arguments: + + Name - Returns the name of the directory entry in the appropriate + format. + +Return Value: + + None. + +--*/ +{ + CHNUM i; + STR buf[80]; + DSTRING tmp_string; + DSTRING tmp2; + CHNUM l; + + if (!_dirent) { + Name->Initialize(""); + return; + } + + if (IsVolumeLabel()) { + memcpy(buf, _dirent, 11); + buf[11] = 0; + + if (buf[0] == 0x05) { + buf[0] = (UCHAR)0xE5; + } + + tmp_string.Initialize(buf); + + l = tmp_string.QueryChCount(); + + for (i = l - 1; i >= 0 && tmp_string.QueryChAt(i) == ' '; i--) { + } + + Name->Initialize(&tmp_string, 0, i + 1); + return; + } + + memcpy(buf, _dirent, 8); + buf[8] = 0; + + if (buf[0] == 0x05) { + buf[0] = (UCHAR)0xE5; + } + + tmp_string.Initialize(buf); + + if (Is8LowerCase()) { + tmp_string.Strlwr(0); + } + + l = tmp_string.QueryChCount(); + + for (i = l - 1; i >= 0 && tmp_string.QueryChAt(i) == ' '; i--) { + } + + Name->Initialize(&tmp_string, 0, i + 1); + + memcpy(buf, &_dirent[8], 3); + buf[3] = 0; + + tmp_string.Initialize(buf); + + if (Is3LowerCase()) { + tmp_string.Strlwr(0); + } + + l = tmp_string.QueryChCount(); + + for (i = l - 1; i >= 0 && tmp_string.QueryChAt(i) == ' '; i--) { + } + + if (i + 1) { + tmp2.Initialize("."); + Name->Strcat(&tmp2); + tmp2.Initialize(&tmp_string, 0, i + 1); + Name->Strcat(&tmp2); + } +} + + +BOOLEAN +FAT_DIRENT::SetName( + IN PCWSTRING Name + ) +/*++ + +Routine Description: + + This routine expects a "compressed" null-terminated name in a format + compatible with the return value of 'QueryName'. + + The validity of the characters in the name will not be checked + by this routine. Only that the name has the appropriate + structure. The routine "IsValidName" will check the validity + of the name characters. + +Arguments: + + Name - Supplies the new name for the directory entry. + +Return Value: + + FALSE - The name was invalid. + TRUE - The name was successfully set. + +--*/ +{ + CHNUM i, j; + STR buf[40]; + CHNUM l; + DSTRING tmp_string; + + if (IsVolumeLabel()) { + if (!Name->QuerySTR( 0, TO_END, buf, 40) || + (i = strlen(buf)) > 11) { + return FALSE; + } + + if (!FAT_SA::IsValidString(Name)) { + return FALSE; + } + + memset(&_dirent[i], ' ', (UINT) (11 - i)); + memcpy(_dirent, buf, (UINT) i); + + if (_dirent[0] == 0xE5) { + _dirent[0] = 0x05; + } + + return TRUE; + } + + l = Name->QueryChCount(); + + if (Name->QueryChAt(0) == '.' && l == 1) { + memcpy(_dirent, ". ", 11); + return TRUE; + } else if (Name->QueryChAt(0) == '.' && + Name->QueryChAt(1) == '.' && + l == 2) { + memcpy(_dirent, ".. ", 11); + return TRUE; + } + + + for (i = 0; i < l && Name->QueryChAt(i) != '.'; i++) { + } + + if (!tmp_string.Initialize(Name, 0, i)) { + return FALSE; + } + + if (!tmp_string.QuerySTR( 0, TO_END, buf, 40)) { + return FALSE; + } + + if ((j = strlen(buf)) > 8) { + return FALSE; + } + + memset(&buf[j], ' ', (UINT) (11 - j)); + + if (i < l) { + for (j = i + 1; j < l && Name->QueryChAt(j) != '.'; j++) { + } + + if (j < l) { + return FALSE; + } + + if (i + 1 < l) { + if (!tmp_string.Initialize(Name, i + 1)) { + return FALSE; + } + + if (!tmp_string.QuerySTR( 0, TO_END, &buf[8], 32)) { + return FALSE; + } + + if ((j = strlen(buf)) > 11) { + return FALSE; + } + + memset(&buf[j], ' ', (UINT) (11 - j)); + } + } + + memcpy(_dirent, buf, 11); + + if (_dirent[0] == 0xE5) { + _dirent[0] = 0x05; + } + + return TRUE; +} + + +BOOLEAN +FAT_DIRENT::IsValidName( + ) CONST +/*++ + +Routine Description: + + This routine verifies that the name is composed of valid + characters. + +Arguments: + + None. + +Return Value: + + FALSE - The name is not valid. + TRUE - The name is valid. + +--*/ +{ + DSTRING tmp; + STR buf[40]; + + if (!_dirent) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return FALSE; + } + + if (IsDot() || IsDotDot()) { + return IsDirectory(); + } + + memcpy(buf, _dirent, 11); + buf[11] = 0; + + if (buf[0] == 0x05) { + buf[0] = (UCHAR)0xE5; + } + + if (!tmp.Initialize(buf)) { + return FALSE; + } + + return FAT_SA::IsValidString(&tmp); +} + + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::IsValidLastWriteTime( + ) CONST +/*++ + +Routine Description: + + This routine verifies the validity of the last write time. + +Arguments: + + None. + +Return Value: + + FALSE - Invalid time stamp. + TRUE - Valid time stamp. + +--*/ +{ + USHORT t; + USHORT d; + + DebugAssert(_dirent); + + memcpy(&t, &_dirent[22], sizeof(USHORT)); // time field + memcpy(&d, &_dirent[24], sizeof(USHORT)); // date field + + return TimeStampsAreValid(t, d); +} + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::QueryLastWriteTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST +/*++ + +Routine Description: + + This routine returns the last write time in the form of a time fields + structure. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + + TIME_FIELDS TimeFields; + USHORT t; + USHORT d; + USHORT year, month, day, hour, minute, second; + + DebugAssert( _dirent ); + DebugPtrAssert( TimeStamp ); + + memcpy(&t, &_dirent[22], sizeof(USHORT)); // time field + memcpy(&d, &_dirent[24], sizeof(USHORT)); // date field + + second = (t&0x001F)*2; // seconds + minute = (t&0x07E0)>>5; // Minutes + hour = t>>11; // Hours + day = d&0x001F; // Day of month 1-31 + month = (d&0x01E0)>>5; // Month + year = (d>>9) + 1980; // Year + + TimeFields.Year = year; + TimeFields.Month = month; + TimeFields.Day = day; + TimeFields.Hour = hour; + TimeFields.Minute = minute; + TimeFields.Second = second; + TimeFields.Milliseconds = 0; + + return RtlTimeFieldsToTime( &TimeFields, (PTIME)TimeStamp ); +} + + + +BOOLEAN +FAT_DIRENT::SetLastWriteTime( + ) +/*++ + +Routine Description: + + This routine sets the last write time to the current date and time. + +Arguments: + + None. + +Return Value: + + FALSE - Time stamp was not set successfully. + TRUE - Time stamp was set successfully. + +--*/ +{ + USHORT fat_time; + USHORT fat_date; + + DebugAssert(_dirent); + + LARGE_INTEGER SystemTime, LocalTime; + TIME_FIELDS Time; + + +#if !defined( _SETUP_LOADER_ ) + IFS_SYSTEM::QueryNtfsTime( &SystemTime ); + RtlSystemTimeToLocalTime( &SystemTime, &LocalTime ); +#else + IFS_SYSTEM::QueryNtfsTime( &LocalTime ); +#endif + + RtlTimeToTimeFields(&LocalTime, &Time); + + fat_time = Time.Second/2; + fat_time |= Time.Minute<<5; + fat_time |= Time.Hour<<11; + + fat_date = Time.Day; + fat_date |= Time.Month<<5; + fat_date |= (Time.Year - 1980)<<9; + + memcpy(&_dirent[22], &fat_time, sizeof(USHORT)); + memcpy(&_dirent[24], &fat_date, sizeof(USHORT)); + + return TRUE; +} + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::IsValidCreationTime( + ) CONST +/*++ + +Routine Description: + + This routine verifies the validity of the creation time. + +Arguments: + + None. + +Return Value: + + FALSE - Invalid time stamp. + TRUE - Valid time stamp. + +--*/ +{ + USHORT t; + USHORT d; + + DebugAssert(_dirent); + + memcpy(&t, &_dirent[14], sizeof(USHORT)); // time field + memcpy(&d, &_dirent[16], sizeof(USHORT)); // date field + + return TimeStampsAreValid(t, d); + +} + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::QueryCreationTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST +/*++ + +Routine Description: + + This routine returns the creation time in the form of a time fields + structure. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + TIME_FIELDS TimeFields; + USHORT t; + USHORT d; + USHORT msec; + USHORT year, month, day, hour, minute, second; + + DebugAssert( _dirent ); + DebugPtrAssert( TimeStamp ); + + memcpy(&t, &_dirent[14], sizeof(USHORT)); // time field + memcpy(&d, &_dirent[16], sizeof(USHORT)); // date field + msec = _dirent[13] * 10; // msec field + + second = (t&0x001F)*2; // seconds + minute = (t&0x07E0)>>5; // Minutes + hour = t>>11; // Hours + day = d&0x001F; // Day of month 1-31 + month = (d&0x01E0)>>5; // Month + year = (d>>9) + 1980; // Year + + if (msec >= 1000) { + second += 1; + msec -= 1000; + } + + TimeFields.Year = year; + TimeFields.Month = month; + TimeFields.Day = day; + TimeFields.Hour = hour; + TimeFields.Minute = minute; + TimeFields.Second = second; + TimeFields.Milliseconds = msec; + + return RtlTimeFieldsToTime( &TimeFields, (PTIME)TimeStamp ); +} + +BOOLEAN +FAT_DIRENT::SetCreationTime( + ) +/*++ + +Routine Description: + + This routine sets the creation time to the current date and time. + +Arguments: + + None. + +Return Value: + + FALSE - Time stamp was not set successfully. + TRUE - Time stamp was set successfully. + +--*/ +{ + USHORT fat_time; + USHORT fat_date; + + DebugAssert(_dirent); + + LARGE_INTEGER SystemTime, LocalTime; + TIME_FIELDS Time; + + +#if !defined( _SETUP_LOADER_ ) + IFS_SYSTEM::QueryNtfsTime( &SystemTime ); + RtlSystemTimeToLocalTime( &SystemTime, &LocalTime ); +#else + IFS_SYSTEM::QueryNtfsTime( &LocalTime ); +#endif + + RtlTimeToTimeFields(&LocalTime, &Time); + + fat_time = Time.Second/2; + fat_time |= Time.Minute<<5; + fat_time |= Time.Hour<<11; + + fat_date = Time.Day; + fat_date |= Time.Month<<5; + fat_date |= (Time.Year - 1980)<<9; + + memcpy(&_dirent[14], &fat_time, sizeof(USHORT)); + memcpy(&_dirent[16], &fat_date, sizeof(USHORT)); + + return TRUE; +} + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::IsValidLastAccessTime( + ) CONST +/*++ + +Routine Description: + + This routine verifies the validity of the last access time. + +Arguments: + + None. + +Return Value: + + FALSE - Invalid time stamp. + TRUE - Valid time stamp. + +--*/ +{ + USHORT t = 0; + USHORT d; + + DebugAssert(_dirent); + + memcpy(&d, &_dirent[18], sizeof(USHORT)); // date field + + return TimeStampsAreValid(t, d); +} + +UFAT_EXPORT +BOOLEAN +FAT_DIRENT::QueryLastAccessTime( + OUT LARGE_INTEGER *TimeStamp + ) CONST +/*++ + +Routine Description: + + This routine returns the last access time in the form of a time fields + structure. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + + TIME_FIELDS TimeFields; + USHORT t; + USHORT d; + USHORT year, month, day, hour, minute, second; + + DebugAssert( _dirent ); + DebugPtrAssert( TimeStamp ); + + t = 0; // no time for last access; just date + memcpy(&d, &_dirent[18], sizeof(USHORT)); // date field + + second = (t&0x001F)*2; // seconds + minute = (t&0x07E0)>>5; // Minutes + hour = t>>11; // Hours + day = d&0x001F; // Day of month 1-31 + month = (d&0x01E0)>>5; // Month + year = (d>>9) + 1980; // Year + + TimeFields.Year = year; + TimeFields.Month = month; + TimeFields.Day = day; + TimeFields.Hour = hour; + TimeFields.Minute = minute; + TimeFields.Second = second; + TimeFields.Milliseconds = 0; + + return RtlTimeFieldsToTime( &TimeFields, (PTIME)TimeStamp ); +} + +BOOLEAN +FAT_DIRENT::SetLastAccessTime( + ) +/*++ + +Routine Description: + + This routine sets the last access time to the current date and time. + +Arguments: + + None. + +Return Value: + + FALSE - Time stamp was not set successfully. + TRUE - Time stamp was set successfully. + +--*/ +{ + USHORT fat_date; + + DebugAssert(_dirent); + + LARGE_INTEGER SystemTime, LocalTime; + TIME_FIELDS Time; + + +#if !defined( _SETUP_LOADER_ ) + IFS_SYSTEM::QueryNtfsTime( &SystemTime ); + RtlSystemTimeToLocalTime( &SystemTime, &LocalTime ); +#else + IFS_SYSTEM::QueryNtfsTime( &LocalTime ); +#endif + + RtlTimeToTimeFields(&LocalTime, &Time); + + fat_date = Time.Day; + fat_date |= Time.Month<<5; + fat_date |= (Time.Year - 1980)<<9; + + memcpy(&_dirent[18], &fat_date, sizeof(USHORT)); + + return TRUE; +} + +VOID +FAT_DIRENT::Destroy( + ) +/*++ + +Routine Description: + + This routine returns the object to its initial state. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _dirent = NULL; +} + +UCHAR +RotateCharRight( + IN UCHAR Char, + IN INT Shift + ) +/*++ + +Routine Description: + + This function rotates an eight-bit character right by the + specified number of bits. + +Arguments: + + Char -- Supplies the character to rotate + Shift -- Supplies the number of bits to shift + +Return Value: + + The rotated character. + +--*/ +{ + UCHAR low_bit; + + Shift %= 8; + + while( Shift-- ) { + + low_bit = Char & 1; + Char >>= 1; + if( low_bit ) { + Char |= 0x80; + } + } + + return (UCHAR)Char; +} + +UCHAR +FAT_DIRENT::QueryChecksum( + ) CONST + +/*++ + +Routine Description: + + This method gets the checksum from the directory entry. If + the entry is a short entry, the checksum is computed; if the + entry is long, the checksum field is returned. + +Arguments: + + None. + +Return Value: + + The directory entry's checksum. + +--*/ +{ + int name_length; + UCHAR sum; + PUCHAR name; + + if( IsLongEntry() ) { + + return( _dirent[13] ); + + } else { + + sum = 0; + name = _dirent; + for( name_length = 11; name_length != 0; name_length-- ) { + + sum = RotateCharRight(sum, 1) + *name++; + } + + return( sum ); + } +} + +BOOLEAN +FAT_DIRENT::IsWellTerminatedLongNameEntry( + ) CONST +/*++ + +Routine Description: + + This method determines whether the entry is a well-terminated + long-name entry. A long-name entry is well terminated if: + + +--*/ +{ + ULONG i; + WCHAR Name[13]; + + if( IsErased() || !IsLongNameEntry() ) { + + return FALSE; + } + + // Assemble the bits and pieces of the name: + // + memcpy( &Name[0], &_dirent[1], 10 ); + memcpy( &Name[5], &_dirent[14], 12 ); + memcpy( &Name[11], &_dirent[28], 4 ); + + if( IsLastLongEntry() ) { + + // Valid syntax for the last name entry is: + // + // N* {0 0xFFFF*} + // + // where N is the set of non-null characters. + // + for( i = 0; i < 13; i++ ) { + + if( Name[i] == 0 ) { + + break; + } + } + + if( i < 13 ) { + + // We hit a null character--step over it. + // + i++; + } + + // The rest of the name-component must be 0xFFFF. + // + for( ; i < 13; i++ ) { + + if( Name[i] != 0xFFFF ) { + + return FALSE; + } + } + + // This name-component was accepted. + // + return TRUE; + + } else { + +#if 0 + // This is an additional consistency check that + // could be performed; however, now (as of 3/28/94) + // the file-system doesn't care, so neither do we. + + // This is not the last component of the name, so + // it can't have any NULL's in it. + // + for( i = 0; i < 13; i++ ) { + + if( Name[i] == 0 ) { + + return FALSE; + } + } +#endif + + return TRUE; + } +} + +BOOLEAN +FAT_DIRENT::QueryLongNameComponent( + OUT PWSTRING NameComponent + ) CONST +/*++ + +Routine Description: + + This method extracts the long-name component from a Long Name + Directory Entry. + +Arguments: + + NameComponent -- Receives the long-name component + +Return Value: + + TRUE upon successful completion. + +--*/ +{ + ULONG i; + WCHAR Name[13]; + + if( IsErased() || !IsLongNameEntry() ) { + + return FALSE; + } + + // Assemble the bits and pieces of the name: + // + memcpy( &Name[0], &_dirent[1], 10 ); + memcpy( &Name[5], &_dirent[14], 12 ); + memcpy( &Name[11], &_dirent[28], 4 ); + + // Long names may be zero terminated; however, if the + // name fits exactly into n long entries will not be + // zero terminated. + // + for( i = 0; i < 13 && Name[i]; i++ ); + + return( NameComponent->Initialize( Name, i ) ); +} + +BOOLEAN +FAT_DIRENT::NameHasTilde( + ) CONST +/*++ + +Routine Description: + + This routine checks a short name entry to see if it contains a tilde. + +Arguments: + + None. + +Return Value: + + TRUE - Tilde. + FALSE - No tilde. + +--*/ +{ + USHORT i; + + if (IsErased() || IsLongNameEntry()) { + return FALSE; + } + + for (i = 0; i < 11; ++i) { + if ('~' == _dirent[i]) { + return TRUE; + } + } + return FALSE; +} + +BOOLEAN +FAT_DIRENT::NameHasExtendedChars( + ) CONST +/*++ + +Routine Description: + + This routine determines whether there are any extended chars + (those with value >= 0x80) in the short file name. + +Arguments: + + None. + +Return Value: + + TRUE - There are one or more extended chars. + FALSE - There are no extended chars. + +--*/ +{ + USHORT i; + + if (IsErased() || IsLongNameEntry()) { + return FALSE; + } + + for (i = 0; i < 11; ++i) { + if (_dirent[i] >= 0x80) { + return TRUE; + } + } + return FALSE; +} + +BOOLEAN +FAT_DIRENT::TimeStampsAreValid( + USHORT t, + USHORT d + ) CONST +/*++ + +Routine Description: + + This routine examines the given time and date fields and + determines whether they represent valid dates. + +Arguments: + + None. + +Return Value: + + TRUE - The time and date are valid. + FALSE - One or both are invalid. + +--*/ +{ + USHORT tmp; + tmp = t&0x001F; // 2-second increments + if (tmp > 29) { + return FALSE; + } + + tmp = (t&0x07E0)>>5; // Minutes + if (tmp > 59) { + return FALSE; + } + + tmp = (t&0xF800)>>11; // Hours + if (tmp > 23) { + return FALSE; + } + + tmp = d&0x001F; // Day of month + if (tmp < 1 || tmp > 31) { + return FALSE; + } + + tmp = (d&0x01E0)>>5; // Month + if (tmp < 1 || tmp > 12) { + return FALSE; + } + + return TRUE; +} diff --git a/private/utils/ufat/src/fatdir.cxx b/private/utils/ufat/src/fatdir.cxx new file mode 100644 index 000000000..3323389f7 --- /dev/null +++ b/private/utils/ufat/src/fatdir.cxx @@ -0,0 +1,201 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" +#include "wstring.hxx" + + +DEFINE_CONSTRUCTOR( FATDIR, OBJECT ); + +UFAT_EXPORT +PVOID +FATDIR::SearchForDirEntry( + IN PCWSTRING FileName + ) +/*++ + +Routine Description: + + This routine gets the directory entry with the (null-terminated) + filename 'FileName'. If no such directory entry exists then + this routine return NULL. + +Arguments: + + FileName - The name of the file searched for. + +Return Value: + + A pointer to a directory entry or NULL. + +--*/ +{ + FAT_DIRENT dirent; + ULONG i; + DSTRING filename; + PVOID p; + + for (i = 0; dirent.Initialize(p = GetDirEntry(i)); i++) { + + if (dirent.IsEndOfDirectory()) { + break; + } + + if (dirent.IsErased()) { + continue; + } + + dirent.QueryName(&filename); + + if (dirent.IsVolumeLabel()) { + continue; + } + + if (*FileName == filename) { + return p; + } + + if (QueryLongName(i, &filename) && (*FileName == filename)) { + return p; + } + } + + return NULL; +} + + +PVOID +FATDIR::GetFreeDirEntry( + ) +/*++ + +Routine Description: + + This routine gets an unused directory entry, if one exists. + If one doesn't exist then this routine returns NULL. + +Arguments: + + None. + +Return Value: + + A pointer to a directory entry or NULL. + +--*/ +{ + FAT_DIRENT dirent; + ULONG i; + PVOID p; + + for (i = 0; dirent.Initialize(p = GetDirEntry(i)); i++) { + + if (dirent.IsEndOfDirectory()) { + if (dirent.Initialize(GetDirEntry(i + 1))) { + dirent.SetEndOfDirectory(); + } + return p; + } + + if (dirent.IsErased()) { + return p; + } + } + + return NULL; +} + + +UFAT_EXPORT +BOOLEAN +FATDIR::QueryLongName( + IN LONG EntryNumber, + OUT PWSTRING LongName + ) +/*++ + +Routine Description: + + This method fetches the long file name associated with the + short directory entry at index EntryNumber. + +Arguments: + + EntryNumber + LongName -- Receives the long name associated with this entry. + Receives "" if there is no long name. + +Return Value: + + TRUE upon successful completion + FALSE to indicate failure or disk corruption. + +--*/ +{ + DSTRING NameComponent; + FAT_DIRENT CurrentEntry; + LONG CurrentIndex; + ULONG Ordinal = 1; + UCHAR Checksum; + + CurrentIndex = EntryNumber; + + if( !CurrentEntry.Initialize( GetDirEntry( CurrentIndex ) ) || + !LongName->Initialize( "" ) ) { + + return FALSE; + } + + Checksum = CurrentEntry.QueryChecksum(); + + CurrentIndex--; + + for( ; CurrentIndex >= 0; CurrentIndex-- ) { + + if( !CurrentEntry.Initialize( GetDirEntry( CurrentIndex ) ) ) { + + return FALSE; + } + + if( CurrentEntry.IsErased() || !CurrentEntry.IsLongEntry() ) { + + // The long name entries are not valid--return an + // empty string for the long name. + // + return( LongName->Initialize( "" ) ); + } + + if( CurrentEntry.IsLongNameEntry() ) { + + if( CurrentEntry.QueryLongOrdinal() != Ordinal || + CurrentEntry.QueryChecksum() != Checksum ) { + + // The long-name entries don't belong to the + // specified short entry. + // + return( LongName->Initialize( "" ) ); + } + + if( !CurrentEntry.QueryLongNameComponent( &NameComponent ) || + !LongName->Strcat( &NameComponent ) ) { + + return FALSE; + } + + if( CurrentEntry.IsLastLongEntry() ) { + + // This was the last entry. + // + return TRUE; + } + + Ordinal++; + } + } + + // There is no long name, or it is not valid. + // + return( LongName->Initialize( "" ) ); +} diff --git a/private/utils/ufat/src/fatsa.cxx b/private/utils/ufat/src/fatsa.cxx new file mode 100644 index 000000000..db3bd7ce5 --- /dev/null +++ b/private/utils/ufat/src/fatsa.cxx @@ -0,0 +1,866 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatsa.cxx + +Author: + + Mark Shavlik (marks) 27-Mar-90 + Norbert Kusters (norbertk) 15-Jan-91 + +Environment: + + ULIB, User Mode + +--*/ + +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "cmem.hxx" +#include "error.hxx" +#include "rtmsg.h" +#include "drive.hxx" +#include "bootfat.h" + +#if !defined(_AUTOCHECK_) && !defined(_SETUP_LOADER_) +#include "timeinfo.hxx" +#endif + + +// Control-C handling is not necessary for autocheck. +#if !defined( _AUTOCHECK_ ) && !defined(_SETUP_LOADER_) + +#include "keyboard.hxx" + +#endif + + +#define CSEC_FAT32MEG 65536 +#define CSEC_FAT16BIT 32680 + +#define MIN_CLUS_BIG 4085 // Minimum clusters for a big FAT. +#define MAX_CLUS_BIG 65525 // Maximum + 1 clusters for big FAT. + + +DEFINE_EXPORTED_CONSTRUCTOR( FAT_SA, SUPERAREA, UFAT_EXPORT ); + +VOID +FAT_SA::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _fat = NULL; + _dir = NULL; +} + + +UFAT_EXPORT +FAT_SA::~FAT_SA( + ) +/*++ + +Routine Description: + + Destructor for FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + +BOOLEAN +FAT_SA::RecoverFile( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine runs through the clusters for the file described by + 'FileName' and takes out bad sectors. + +Arguments: + + FullPathFileName - Supplies a full path name of the file to recover. + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _SETUP_LOADER_ ) + + return FALSE; + +#else // _SETUP_LOADER_ + + + HMEM hmem; + USHORT clus; + BOOLEAN changes; + PFATDIR fatdir; + BOOLEAN need_delete; + FAT_DIRENT dirent; + ULONG old_file_size; + ULONG new_file_size; + + if ((clus = QueryFileStartingCluster(FullPathFileName, + &hmem, + &fatdir, + &need_delete, + &dirent)) == 1) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", FullPathFileName); + return FALSE; + } + + if (clus == 0xFFFF) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (clus == 0) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", FullPathFileName); + return FALSE; + } + + if (dirent.IsDirectory()) { + old_file_size = _drive->QuerySectorSize()* + QuerySectorsPerCluster()* + _fat->QueryLengthOfChain(clus); + } else { + old_file_size = dirent.QueryFileSize(); + } + + if (!RecoverChain(&clus, &changes)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (dirent.IsDirectory() || changes) { + new_file_size = _drive->QuerySectorSize()* + QuerySectorsPerCluster()* + _fat->QueryLengthOfChain(clus); + } else { + new_file_size = old_file_size; + } + + if (changes) { + + +// Autochk doesn't need control C handling. +#if !defined( _AUTOCHECK_ ) + + // Disable contol-C handling and + + if (!KEYBOARD::EnableBreakHandling()) { + Message->Set(MSG_CANT_LOCK_THE_DRIVE); + Message->Display(""); + return FALSE; + } + +#endif + + + // Lock the drive in preparation for writes. + + if (!_drive->Lock()) { + Message->Set(MSG_CANT_LOCK_THE_DRIVE); + Message->Display(""); + return FALSE; + } + + dirent.SetStartingCluster(clus); + + dirent.SetFileSize(new_file_size); + + if (!fatdir->Write()) { + return FALSE; + } + + if (!Write(Message)) { + return FALSE; + } + + +// Autochk doesn't need control C handling. +#if !defined( _AUTOCHECK_ ) + + KEYBOARD::DisableBreakHandling(); + +#endif + + + } + + Message->Set(MSG_RECOV_BYTES_RECOVERED); + Message->Display("%d%d", new_file_size, old_file_size); + + + if (need_delete) { + DELETE(fatdir); + } + + return TRUE; + +#endif // _SETUP_LOADER_ +} + + +SECTORCOUNT +FAT_SA::QueryFreeSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of unused sectors on disk. + +Arguments: + + None. + +Return Value: + + The number of free sectors on disk. + +--*/ +{ + if (!_fat) { + perrstk->push(ERR_NOT_READ, QueryClassId()); + return 0; + } + + return _fat->QueryFreeClusters()*QuerySectorsPerCluster(); +} + + +FATTYPE +FAT_SA::QueryFatType( + ) CONST +/*++ + +Routine Description: + + This routine computes the FATTYPE of the FAT for this volume. + +Arguments: + + None. + +Return Value: + + The FATTYPE for the FAT. + +--*/ +{ + return _ft; +} + + + +#if !defined( _AUTOCHECK_ ) && !defined(_SETUP_LOADER_) + +BOOLEAN +FAT_SA::QueryLabel( + OUT PWSTRING Label + ) CONST +/*++ + +Routine Description: + + This routine queries the label from the FAT superarea. + If the label is not present then 'Label' will return the null-string. + If the label is invalid then FALSE will be returned. + +Arguments: + + Label - Returns a volume label. + +Return Value: + + FALSE - The label is invalid. + TRUE - The label is valid. + +--*/ +{ + return QueryLabel(Label, NULL); +} + + +BOOLEAN +FAT_SA::QueryLabel( + OUT PWSTRING Label, + OUT PTIMEINFO TimeInfo + ) CONST +/*++ + +Routine Description: + + This routine queries the label from the FAT superarea. + If the label is not present then 'Label' will return the null-string. + If the label is invalid then FALSE will be returned. + +Arguments: + + Label - Returns a volume label. + +Return Value: + + FALSE - The label is invalid. + TRUE - The label is valid. + +--*/ +{ + INT i; + FAT_DIRENT dirent; + FILETIME TimeStamp; + + DebugAssert(_dir); + + for (i = 0; ; i++) { + if (!dirent.Initialize(_dir->GetDirEntry(i)) || + dirent.IsEndOfDirectory()) { + return Label->Initialize(""); + } + + if (!dirent.IsErased() && dirent.IsVolumeLabel()) { + break; + } + } + + dirent.QueryName(Label); + + if ( TimeInfo ) { + return ( dirent.QueryLastWriteTime( (LARGE_INTEGER *)&TimeStamp ) && + TimeInfo->Initialize( &TimeStamp ) ); + } + + return TRUE; +} + +#else // _AUTOCHECK_ or _SETUP_LOADER_ is defined + +BOOLEAN +FAT_SA::QueryLabel( + OUT PWSTRING Label + ) CONST +{ + INT i; + FAT_DIRENT dirent; + + DebugAssert(_dir); + + for (i = 0; ; i++) { + if (!dirent.Initialize(_dir->GetDirEntry(i)) || + dirent.IsEndOfDirectory()) { + return Label->Initialize(""); + } + + if (!dirent.IsErased() && dirent.IsVolumeLabel()) { + break; + } + } + + dirent.QueryName(Label); + + return TRUE; +} + + +#endif // _AUTOCHECK_ + + +BOOLEAN +FAT_SA::SetLabel( + IN PCWSTRING NewLabel + ) +/*++ + +Routine Description: + + This routine sets the label for a FAT partition. + +Arguments: + + NewLabel - Supplies the new volume label. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + FAT_DIRENT dirent; + INT i; + DSTRING label; + + if (!_dir) { + return FALSE; + } + + if (!label.Initialize(NewLabel)) { + return FALSE; + } + + if (!label.Strupr()) { + return FALSE; + } + + if (!IsValidString(&label)) { + return FALSE; + } + + for (i = 0; dirent.Initialize(_dir->GetDirEntry(i)); i++) { + if (dirent.IsEndOfDirectory()) { + break; + } + + if (dirent.IsErased()) { + continue; + } + + if (dirent.IsVolumeLabel()) { + if (!label.QueryChCount()) { + dirent.SetErased(); + return TRUE; + } + + return (BOOLEAN) (dirent.SetLastWriteTime() && + dirent.SetName(&label)); + } + } + + if (!label.QueryChCount()) { + return TRUE; + } + + if (!dirent.Initialize(_dir->GetFreeDirEntry())) { + return FALSE; + } + + dirent.Clear(); + dirent.SetVolumeLabel(); + + return (BOOLEAN) (dirent.SetLastWriteTime() && dirent.SetName(&label)); +} + + +UFAT_EXPORT +USHORT +FAT_SA::QueryFileStartingCluster( + IN PCWSTRING FullPathFileName, + OUT PHMEM Hmem, + OUT PPFATDIR Directory, + OUT PBOOLEAN DeleteDirectory, + OUT PFAT_DIRENT DirEntry + ) +/*++ + +Routine Description: + + This routine computes the starting cluster number of the file described + by 'FileName' by tracing through the directories leading to the file. + +Arguments: + + FullPathFileName - Supplies a full path file name that starts with + a '\' (i.e. no drive spec). + +Return Value: + + The starting cluster for the file or 1 if the file is not found or + 0xFFFF if there was an error. + +--*/ +{ + CHNUM i, j, l; + DSTRING component; + USHORT clus; + FAT_DIRENT the_dirent; + PFILEDIR filedir; + PFAT_DIRENT dirent; + HMEM hmem; + + + DebugAssert(_dir); + DebugAssert(_fat); + + filedir = NULL; + + if (!Hmem) { + Hmem = &hmem; + } + + if (DirEntry) { + dirent = DirEntry; + } else { + dirent = &the_dirent; + } + + l = FullPathFileName->QueryChCount(); + + for (i = 0; i < l && FullPathFileName->QueryChAt(i) != '\\'; i++) { + } + + if (i == l) { + return 0xFFFF; + } + + if (i == l - 1) { // root directory + return 0; + } + + j = ++i; + for (; i < l && FullPathFileName->QueryChAt(i) != '\\'; i++) { + } + + if (!component.Initialize(FullPathFileName, j, i - j) || + !component.Strupr()) { + return 1; + } + + if (!dirent->Initialize(_dir->SearchForDirEntry(&component))) { + return 1; + } + + if (!(clus = dirent->QueryStartingCluster())) { + return 0; + } + + while (i < l) { + + if (!filedir && + !(filedir = NEW FILEDIR)) { + return 0xFFFF; + } + + if (!Hmem->Initialize() || + !filedir->Initialize(Hmem, _drive, this, _fat, clus)) { + return 0xFFFF; + } + + if (!filedir->Read()) { + return 1; + } + + j = ++i; + for (; i < l && FullPathFileName->QueryChAt(i) != '\\'; i++) { + } + + if (!component.Initialize(FullPathFileName, j, i - j) || + !component.Strupr()) { + return 0xFFFF; + } + + if (!dirent->Initialize(filedir->SearchForDirEntry(&component))) { + return 1; + } + + if (!(clus = dirent->QueryStartingCluster())) { + return 1; + } + } + + if (Directory) { + if (filedir) { + *Directory = filedir; + if (DeleteDirectory) { + *DeleteDirectory = TRUE; + } + } else { + *Directory = _dir; + if (DeleteDirectory) { + *DeleteDirectory = FALSE; + } + } + } else { + DELETE(filedir); + } + + return clus; +} + + +VOID +FAT_SA::Destroy( + ) +/*++ + +Routine Description: + + This routine cleans up the local data in the fat super area. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DELETE(_fat); + DELETE(_dir); +} + + +FATTYPE +FAT_SA::ComputeFatType( + ) CONST +/*++ + +Routine Description: + + Given the total number of clusters on the disk, this routine computes + whether the FAT will be a 16 bit FAT or a 12 bit FAT. + +Arguments: + + ClusterCount - Supplies the number of clusters on the disk. + +Return Value: + + SMALL - A 12 bit FAT is required. + LARGE - A 16 bit FAT is required. + +--*/ +{ + return ComputeSystemId() == SYSID_FAT12BIT ? SMALL : LARGE; +} + + +PARTITION_SYSTEM_ID +FAT_SA::ComputeSystemId( + ) CONST +/*++ + +Routine Description: + + This routine computes the system id for a FAT file system with + the given number of sectors. + +Arguments: + + None. + +Return Value: + + The correct system id for this partition. + +--*/ +{ + SECTORCOUNT disk_size; + + disk_size = QueryVirtualSectors(); + + return disk_size < CSEC_FAT16BIT ? SYSID_FAT12BIT : + disk_size < CSEC_FAT32MEG ? SYSID_FAT16BIT : + SYSID_FAT32MEG; +} + +#if !defined(_SETUP_LOADER_) + +USHORT +FAT_SA::ComputeRootEntries( + ) CONST +/*++ + +Routine Description: + + This routine uses the size of the disk and a standard table in + order to compute the required number of root directory entries. + +Arguments: + + None. + +Return Value: + + The required number of root directory entries. + +--*/ +{ + switch (_drive->QueryMediaType()) { + + case F3_720_512: + case F5_360_512: + case F5_320_512: + case F5_320_1024: + case F5_180_512: + case F5_160_512: + return 112; + + case F5_1Pt2_512: + case F3_1Pt44_512: + return 224; + + case F3_2Pt88_512: + case F3_20Pt8_512: + return 240; + } + + return 512; +} + + +USHORT +FAT_SA::ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ) +/*++ + +Routine Description: + + This routine computes the number of sectors per cluster required + based on the actual number of sectors. + +Arguments: + + Sectors - Supplies the total number of sectors on the disk. + FatType - Supplies the type of FAT. + MediaType - Supplies the type of the media. + +Return Value: + + The required number of sectors per cluster. + +--*/ +{ + USHORT sec_per_clus; + SECTORCOUNT threshold; + + if (FatType == SMALL) { + threshold = MIN_CLUS_BIG; + sec_per_clus = 1; + } else { + threshold = MAX_CLUS_BIG; + sec_per_clus = 1; + } + + while (Sectors >= threshold) { + sec_per_clus *= 2; + threshold *= 2; + } + + switch (MediaType) { + + case F5_320_512: + case F5_360_512: + case F3_720_512: + case F3_2Pt88_512: + sec_per_clus = 2; + break; + + case F3_20Pt8_512: + sec_per_clus = 4; + break; + + default: + break; + + } + + return sec_per_clus; +} + +#endif // _SETUP_LOADER_ + + +BOOLEAN +FAT_SA::IsValidString( + IN PCWSTRING String + ) +/*++ + +Routine Description: + + This routine determines whether or not the given null-terminated string + has any invalid characters in it. + +Arguments: + + String - Supplies the string to validate. + +Return Value: + + FALSE - The string contains invalid characters. + TRUE - The string is free from invalid characters. + +Notes: + + The list of invalid characters is stricter than HPFS requires. + +--*/ +{ + CHNUM i, l; + + l = String->QueryChCount(); + + for (i = 0; i < l; i++) { + if (String->QueryChAt(i) < 32) { + return FALSE; + } + + switch (String->QueryChAt(i)) { + case '*': + case '?': + case '/': + case '\\': + case '|': + case ',': + case ';': + case ':': + case '+': + case '=': + case '<': + case '>': + case '[': + case ']': + case '"': + case '.': + return FALSE; + } + } + + return TRUE; +} diff --git a/private/utils/ufat/src/fatsachk.cxx b/private/utils/ufat/src/fatsachk.cxx new file mode 100644 index 000000000..e285b2cdc --- /dev/null +++ b/private/utils/ufat/src/fatsachk.cxx @@ -0,0 +1,3246 @@ +#include <pch.cxx> + +#include "bitvect.hxx" +#include "error.hxx" +#include "intstack.hxx" +#include "rtmsg.h" +#include "ifsentry.hxx" + + +// Timeinfo is full of windows stuff. +#if !defined( _AUTOCHECK_ ) && !defined( _SETUP_LOADER_ ) + +#include "timeinfo.hxx" + +#endif + +#define FAT_BPB_RESERVED_DIRTY 0x01 +#define FAT_BPB_RESERVED_TEST_SURFACE 0x02 + +extern "C" { + #include <stdio.h> +} + + +VOID +dofmsg( + IN PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +{ + if (*NeedErrorsMessage) { + Message->Set(MSG_CORRECTIONS_WILL_NOT_BE_WRITTEN, NORMAL_MESSAGE, TEXT_MESSAGE); + Message->Display(""); + *NeedErrorsMessage = FALSE; + } +} + +STATIC VOID +EraseAssociatedLongName( + PFATDIR Dir, + INT FirstLongEntry, + INT ShortEntry + ) +{ + FAT_DIRENT dirent; + + for (int j = FirstLongEntry; j < ShortEntry; ++j) { + dirent.Initialize(Dir->GetDirEntry(j)); + dirent.SetErased(); + } +} + +STATIC BOOLEAN +IsString8Dot3( + PCWSTRING s + ) +/*++ + +Routine Description: + + This routine is used to ensure that lfn's legally correspond + to their short names. The given string is examined to see if it + is a legal fat 8.3 name. + +Arguments: + + s -- lfn to examine. + +Return Value: + + TRUE - The string is a legal 8.3 name. + FALSE - Not legal. + +--*/ +{ + USHORT i; + BOOLEAN extension_present = FALSE; + WCHAR c; + + // + // The name can't be more than 12 characters (including a single dot). + // + + if (s->QueryChCount() > 12) { + return FALSE; + } + + for (i = 0; i < s->QueryChCount(); ++i) { + + c = s->QueryChAt(i); + +#if 0 + if (!FsRtlIsAnsiCharLegalFat(c, FALSE) { + return FALSE; + } +#endif + + if (c == '.') { + + // + // We stepped onto a period. We require the following things: + // + // - it can't be the first character + // - there can be only one + // - there can't be more that 3 characters following it + // - the previous character can't be a space + // + + if (i == 0 || + extension_present || + s->QueryChCount() - (i + 1) > 3 || + s->QueryChAt(i - 1) == ' ') { + + return FALSE; + } + extension_present = TRUE; + } + + // + // The base part of the name can't be more than 8 characters long. + // + + if (i >= 8 && !extension_present) { + return FALSE; + } + + } + + // + // The name cannot end in a space or a period. + // + + if (c == ' ' || c == '.') { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +FAT_SA::VerifyAndFix( + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN BOOLEAN Verbose, + IN BOOLEAN OnlyIfDirty, + IN BOOLEAN RecoverFree, + IN BOOLEAN RecoverAlloc, + IN BOOLEAN /* ResizeLogFile */, + IN ULONG /* LogFileSize */, + OUT PULONG ExitStatus, + IN PCWSTRING DriveLetter + ) +/*++ + +Routine Description: + + This routine verifies the FAT superarea and if neccessary fixes + it to a correct state. + +Arguments: + + FixLevel - Supplies the level of fixes that may be performed on + the disk. + Message - Supplies an outlet for messages. + Verbose - Supplies whether or not to be verbose. + OnlyIfDirty - Supplies whether or not to continue if the volume + is clean. + RecoverFree - Supplies whether or not to recover the free sectors on + the volume. + RecoverAlloc - Supplies whether or not to recover the allocated + sectors on the volume. + ExitStatus - Returns an indication of the result of the check + DriveLetter - For autocheck, supplies the letter of the volume + being checked + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + FAT_DIRENT eadent; + ULONG cluster_size; + USHORT ea_clus_num; + USHORT num_eas; + PEA_INFO ea_infos; + USHORT cluster_count; + HMEM ea_header_mem; + EA_HEADER ea_header; + BOOLEAN changes; + BITVECTOR fat_bitmap; + FATCHK_REPORT report; + PUSHORT p; + VOLID volid; + USHORT free_count, bad_count, total_count; + BOOLEAN fmsg; + DSTRING label; + DSTRING eafilename; + DSTRING eafilepath; + BOOLEAN tmp_bool; + USHORT tmp_ushort; + DSTRING date; + DSTRING time; + UCHAR dirty_byte, media_byte; + + if (NULL == ExitStatus) { + ExitStatus = &report.ExitStatus; + } + report.ExitStatus = CHKDSK_EXIT_SUCCESS; + *ExitStatus = CHKDSK_EXIT_COULD_NOT_CHK; + + fmsg = TRUE; + + if (FixLevel != CheckOnly) { + fmsg = FALSE; + } + + if (QueryLength() <= SecPerBoot()) { + Message->Set(MSG_NOT_FAT); + Message->Display(""); + return FALSE; + } + + // + // Check to see if the dirty bit is set. + // + + if (OnlyIfDirty) { + dirty_byte = QueryVolumeFlags(); + + if ((dirty_byte & + (FAT_BPB_RESERVED_DIRTY | FAT_BPB_RESERVED_TEST_SURFACE)) == 0) { + Message->Set(MSG_CHK_VOLUME_CLEAN); + Message->Display(); + *ExitStatus = CHKDSK_EXIT_SUCCESS; + return TRUE; + } + + // If the second bit of the dirty byte is set then + // also perform a full recovery of the free and allocated + // space. + + if (dirty_byte & FAT_BPB_RESERVED_TEST_SURFACE) { + RecoverFree = TRUE; + RecoverAlloc = TRUE; + } + + // + // The volume is not clean, so if we're autochecking we want to + // make sure that we're printing real messages on the console + // instead of just dots. + // + +#ifdef _AUTOCHECK_ + + BOOLEAN bPrev; + + Message->SetLoggingEnabled(); + + bPrev = Message->SetDotsOnly(FALSE); + + if (bPrev) { + + if (NULL != DriveLetter) { + Message->Set(MSG_CHK_RUNNING); + Message->Display("%W", DriveLetter); + } + + Message->Set(MSG_FILE_SYSTEM_TYPE); + Message->Display("%s", "FAT"); + } + +#endif /* _AUTOCHECK */ + } + + + // The BPB's Media Byte must be in the set accepted + // by the file system. + // + media_byte = QueryMediaByte(); + + if ((media_byte != 0xf0) && + (media_byte != 0xf8) && + (media_byte != 0xf9) && + (media_byte != 0xfc) && + (media_byte != 0xfd) && + (media_byte != 0xfe) && + (media_byte != 0xff)) { + + SetMediaByte(_drive->QueryMediaByte()); + } + + + // First print out the label and volume serial number. + +// We won't bother printing this message under autocheck. +#if !defined( _AUTOCHECK_ ) && !defined( _SETUP_LOADER_ ) + + TIMEINFO timeinfo; + + if ((QueryLabel(&label, &timeinfo) || label.Initialize("")) && + label.QueryChCount() && + timeinfo.QueryDate(&date) && + timeinfo.QueryTime(&time)) { + + Message->Set(MSG_VOLUME_LABEL_AND_DATE); + Message->Display("%W%W%W", &label, &date, &time); + } + +#else +#if 0 + + if (QueryLabel(&label) && label.QueryChCount() > 0) { + Message->Set(MSG_VOLUME_LABEL); + Message->Display("%W", &label); + } + +#endif +#endif // !_AUTOCHECK_ && !_SETUP_LOADER_ + + + if (volid = QueryVolId()) { + p = (PUSHORT) &volid; + Message->Set(MSG_VOLUME_SERIAL_NUMBER); + Message->Display("%04X%04X", p[1], p[0]); + } + + + + // Validate the FAT. + + _fat->Scrub(&changes); + + if (changes) { + dofmsg(Message, &fmsg); + Message->Set(MSG_CHK_ERRORS_IN_FAT); + Message->Display(""); + } + + + // Make sure that the media type in the BPB is the same as at + // the beginning of the FAT. + + if (QueryMediaByte() != _fat->QueryMediaByte()) { + dofmsg(Message, &fmsg); + Message->Set(MSG_PROBABLE_NON_DOS_DISK); + Message->Display(""); + if (!Message->IsYesResponse(FALSE)) { + report.ExitStatus = CHKDSK_EXIT_SUCCESS; + return TRUE; + } + + _fat->SetEarlyEntries(QueryMediaByte()); + } + + + // Compute the cluster size and the number of clusters on disk. + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + cluster_count = QueryClusterCount(); + + + // No EAs have been detected yet. + + ea_infos = NULL; + num_eas = 0; + + + // Create an EA file name string. + + if (!eafilename.Initialize("EA DATA. SF") || + !eafilepath.Initialize("\\EA DATA. SF")) { + return FALSE; + } + + + // This bitmap will be reinitialized before 'WalkDirectoryTree'. + // Its contents will be ignored until then. + + if (!fat_bitmap.Initialize(cluster_count)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + + // If there is an EA file on disk then... + + if (eadent.Initialize(_dir->SearchForDirEntry(&eafilename))) { + + + // Validate the EA file directory entry. + + if (!ValidateDirent(&eadent, &eafilepath, FixLevel, FALSE, Message, + &fmsg, &fat_bitmap, &tmp_bool, &tmp_ushort, + ExitStatus)) { + return FALSE; + } + + + // If the EA file directory entry was valid then... + + if (!eadent.IsErased()) { + + + // The EA file should not have an EA handle. + + if (eadent.QueryEaHandle()) { + dofmsg(Message, &fmsg); + Message->Set(MSG_CHK_EAFILE_HAS_HANDLE); + Message->Display(""); + eadent.SetEaHandle(0); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + + // Compute the EA file's starting cluster. + + ea_clus_num = eadent.QueryStartingCluster(); + + + // Perform any log operations recorded at the beginning + // of the EA file. + + if (!PerformEaLogOperations(ea_clus_num, FixLevel, + Message, &fmsg)) { + return FALSE; + } + + + // Validate the EA file's EA sets and return an array of + // information about them. + + ea_infos = RecoverEaSets(ea_clus_num, &num_eas, FixLevel, + Message, &fmsg); + + + // If there are no valid EAs in the EA file then erase + // the EA file. + + if (!ea_infos) { + + if (num_eas) { + return FALSE; + } + + eadent.SetErased(); + + dofmsg(Message, &fmsg); + Message->Set(MSG_CHK_EMPTY_EA_FILE); + Message->Display(""); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + } + } + + + // Initialize FAT bitmap to be used in detection of cross-links. + + if (!fat_bitmap.Initialize(cluster_count)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + DELETE(ea_infos); + return FALSE; + } + + if (!CheckSectorHeapAllocation(FixLevel, Message, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + if (!VerifyFatExtensions(FixLevel, Message, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + // Validate all of the files on the disk. + + if (!WalkDirectoryTree(ea_infos, num_eas, &fat_bitmap, &report, + FixLevel, RecoverAlloc, Message, Verbose, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + + // If there are EAs on the disk then... + + if (ea_infos) { + + // Remove all unused EAs from EA file. + + if (!PurgeEaFile(ea_infos, num_eas, &fat_bitmap, FixLevel, Message, + &fmsg)) { + DELETE( ea_infos ); + return FALSE; + } + + + // Rebuild header portion of EA file. + + if (!ea_header_mem.Initialize() || + !RebuildEaHeader(&ea_clus_num, ea_infos, num_eas, + &ea_header_mem, &ea_header, &fat_bitmap, + FixLevel, Message, &fmsg)) { + DELETE( ea_infos ); + return FALSE; + } + + if (ea_clus_num) { + eadent.SetStartingCluster(ea_clus_num); + eadent.SetFileSize(cluster_size* + _fat->QueryLengthOfChain(ea_clus_num)); + } else { + dofmsg(Message, &fmsg); + Message->Set(MSG_CHK_EMPTY_EA_FILE); + Message->Display(""); + + eadent.SetErased(); + } + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + // + // If WalkDirectoryTree deleted any files, we need to sync the + // FAT_EXTENSIONS up with the FAT again. + // + + if (!VerifyFatExtensions(FixLevel, Message, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + + if (!RecoverOrphans(&fat_bitmap, FixLevel, Message, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + // + // RecoverOrphans may have cleared faulty entries from the FAT, + // and now we need to sync the FAT_EXTENSIONS again. + // + + if (!VerifyFatExtensions(FixLevel, Message, &fmsg)) { + DELETE(ea_infos); + return FALSE; + } + + + // If requested, validate all of the free space on the volume. + + if (RecoverFree && !RecoverFreeSpace(Message)) { + DELETE(ea_infos); + return FALSE; + } + + total_count = cluster_count - FirstDiskCluster; + + Message->Set(MSG_TOTAL_DISK_SPACE); + Message->Display("%9u", cluster_size*total_count); + + if (report.HiddenEntriesCount) { + Message->Set(MSG_HIDDEN_FILES); + Message->Display("%9u%d", + cluster_size*report.HiddenClusters, report.HiddenEntriesCount); + } + + if (report.DirEntriesCount) { + Message->Set(MSG_DIRECTORIES); + Message->Display("%9u%d", + cluster_size*report.DirClusters, report.DirEntriesCount); + } + + if (report.FileEntriesCount) { + Message->Set(MSG_USER_FILES); + Message->Display("%9u%d", + cluster_size*report.FileClusters, report.FileEntriesCount); + } + + if (bad_count = _fat->QueryBadClusters()) { + Message->Set(MSG_BAD_SECTORS); + Message->Display("%9u", cluster_size*bad_count); + } + + if (ea_infos) { + Message->Set(MSG_CHK_EA_SIZE); + Message->Display("%9u", + cluster_size*_fat->QueryLengthOfChain(ea_clus_num)); + } + + free_count = _fat->QueryFreeClusters(); + + Message->Set(MSG_AVAILABLE_DISK_SPACE); + Message->Display("%9u", cluster_size*free_count); + + Message->Set(MSG_ALLOCATION_UNIT_SIZE); + Message->Display("%9u", cluster_size); + + Message->Set(MSG_TOTAL_ALLOCATION_UNITS); + Message->Display("%9u", total_count); + + Message->Set(MSG_AVAILABLE_ALLOCATION_UNITS); + Message->Display("%9u", free_count); + + + if (FixLevel != CheckOnly && ea_infos && !ea_header.Write()) { + DELETE(ea_infos); + return FALSE; + } + + // Clear the dirty bit. + // + if( RecoverAlloc ) { + SetVolumeFlags(FAT_BPB_RESERVED_DIRTY | FAT_BPB_RESERVED_TEST_SURFACE, + TRUE); + } else { + SetVolumeFlags(FAT_BPB_RESERVED_DIRTY, TRUE); + } + + + if (FixLevel != CheckOnly && !Write(Message)) { + DELETE(ea_infos); + return FALSE; + } + + if (changes) { + report.ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + *ExitStatus = report.ExitStatus; + + DELETE(ea_infos); + return TRUE; +} + + +BOOLEAN +FAT_SA::PerformEaLogOperations( + IN USHORT EaFileCn, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine reads the EA file log from the disk and then performs + any logged operations specified. + +Arguments: + + EaFileCn - Supplies the first cluster of the EA file. + FixLevel - Supplies the fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has occurred + under check only conditions. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + HMEM hmem; + EA_HEADER ea_header; + PEA_FILE_HEADER pea_header; + ULONG cluster_size; + USHORT num_clus; + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + num_clus = sizeof(EA_FILE_HEADER) + BaseTableSize*sizeof(USHORT); + if (num_clus%cluster_size) { + num_clus = (USHORT) (num_clus/cluster_size + 1); + } else { + num_clus = (USHORT) (num_clus/cluster_size); + } + + if (!hmem.Initialize() || + !ea_header.Initialize(&hmem, _drive, this, _fat, EaFileCn, num_clus) || + !(pea_header = ea_header.GetEaFileHeader())) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!ea_header.Read()) { + Message->Set(MSG_CHK_CANT_CHECK_EA_LOG); + Message->Display(""); + return TRUE; + } + + if (pea_header->Signature != HeaderSignature || + pea_header->FormatType || + pea_header->LogType) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_BAD_LOG, NORMAL_MESSAGE, TEXT_MESSAGE); + Message->Display(""); + if (Message->IsYesResponse(TRUE)) { + pea_header->Signature = HeaderSignature; + pea_header->Cluster1 = 0; + pea_header->Cluster2 = 0; + pea_header->Cluster3 = 0; + + if (FixLevel != CheckOnly) { + ea_header.Write(); + } + + return TRUE; + } else { + return FALSE; + } + } + + if (pea_header->Cluster1) { + if (_fat->IsInRange(pea_header->Cluster1) && + _fat->IsInRange(pea_header->NewCValue1)) { + _fat->SetEntry(pea_header->Cluster1, pea_header->NewCValue1); + } else { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERROR_IN_LOG); + Message->Display(""); + } + } + + if (pea_header->Cluster2) { + if (_fat->IsInRange(pea_header->Cluster2) && + _fat->IsInRange(pea_header->NewCValue2)) { + _fat->SetEntry(pea_header->Cluster2, pea_header->NewCValue2); + } else { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERROR_IN_LOG); + Message->Display(""); + } + } + + if (pea_header->Cluster3) { + if (_fat->IsInRange(pea_header->Cluster3) && + _fat->IsInRange(pea_header->NewCValue3)) { + _fat->SetEntry(pea_header->Cluster3, pea_header->NewCValue3); + } else { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERROR_IN_LOG); + Message->Display(""); + } + } + + return TRUE; +} + + +PEA_INFO +FAT_SA::RecoverEaSets( + IN USHORT EaFileCn, + OUT PUSHORT NumEas, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine validates and if necessary recovers the EA file. + +Arguments: + + EaFileCn - Supplies the cluster number for the EA file. + NumEas - Returns the number of EA sets in the EA file. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has occurred + under check only conditions. + +Return Value: + + An allocated array containing 'NumberOfEaSets' entries documenting + important information about the EA sets. If there are no EAs then + 'NumberOfEaSets' is returned as 0 and NULL is returned. If there + is an error then NULL will be returned with a non-zero + 'NumberOfEaSets'. + +--*/ +{ + PEA_INFO ea_infos; + USHORT clus, prev; + USHORT num_eas; + USHORT i; + + DebugAssert(NumEas); + + *NumEas = 1; + + ea_infos = NEW EA_INFO[_fat->QueryLengthOfChain(EaFileCn)]; + if (!ea_infos) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return NULL; + } + + + // Scan file for EA sets and validate them while updating the + // array. + + num_eas = 0; + prev = EaFileCn; + while (!_fat->IsEndOfChain(prev)) { + + clus = VerifyAndFixEaSet(prev, &ea_infos[num_eas], FixLevel, + Message, NeedErrorsMessage); + + if (clus) { + num_eas++; + } else { + clus = _fat->QueryEntry(prev); + } + + prev = clus; + } + + if (!num_eas) { + DELETE( ea_infos ); + *NumEas = 0; + return NULL; + } + + + // Go through and remove unused portions of the EA file. + + for (i = 0; i < (USHORT)(num_eas - 1); i++) { + if (ea_infos[i].LastCn != ea_infos[i + 1].PreceedingCn) { + + _fat->RemoveChain(ea_infos[i].LastCn, + ea_infos[i + 1].PreceedingCn); + + dofmsg(Message, NeedErrorsMessage); + + Message->Set(MSG_CHK_UNUSED_EA_PORTION); + Message->Display(""); + + ea_infos[i + 1].PreceedingCn = ea_infos[i].LastCn; + } + } + + if (!_fat->IsEndOfChain(ea_infos[num_eas - 1].LastCn)) { + + _fat->SetEndOfChain(ea_infos[num_eas - 1].LastCn); + + dofmsg(Message, NeedErrorsMessage); + + Message->Set(MSG_CHK_UNUSED_EA_PORTION); + Message->Display(""); + } + + + // Sort the EAs in the EA file. + + if (!EaSort(ea_infos, num_eas, Message, NeedErrorsMessage)) { + return NULL; + } + + *NumEas = num_eas; + + return ea_infos; +} + + +USHORT +FAT_SA::VerifyAndFixEaSet( + IN USHORT PreceedingCluster, + OUT PEA_INFO EaInfo, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine attempts to identify the clusters following the + 'PreceedingCluster' as an EA set. If this routine does not + recognize these clusters as an EA set then it will return 0. + Otherwise, it will return the last cluster of the validated EA set. + + Changes may be made to the clusters if they are recognized as an EA + set with errors. + +Arguments: + + PreceedingCluster - Supplies the cluster preceeding the EA set cluster. + Info - Returns information about the EA set. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not errors have occurred + under check only conditions. + +Return Value: + + The cluster number of the last cluster in the EA set or 0. + +--*/ +{ + HMEM hmem; + EA_SET easet; + USHORT clus; + PEA_HDR eahdr; + LONG i; + USHORT j; + ULONG need_count; + LONG total_size; + LONG size; + USHORT length; + BOOLEAN need_write; + PEA pea; + BOOLEAN more; + USHORT chain_length; + + clus = _fat->QueryEntry(PreceedingCluster); + chain_length = _fat->QueryLengthOfChain(clus); + + length = 1; + need_write = FALSE; + + if (!hmem.Initialize() || + !easet.Initialize(&hmem, _drive, this, _fat, clus, length) || + !(eahdr = easet.GetEaSetHeader())) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return 0; + } + + if (!easet.Read()) { + return 0; + } + + if (!easet.VerifySignature()) { + return 0; + } + + need_count = 0; + total_size = 4; + for (i = 0; ; i++) { + for (j = 0; !(pea = easet.GetEa(i, &size, &more)) && more && + length + j < chain_length; ) { + j++; + if (!hmem.Initialize() || + !easet.Initialize(&hmem, _drive, this, _fat, clus, length + j)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return 0; + } + + if (!easet.Read()) { + return 0; + } + } + + if (pea) { + length += j; + } else { + break; + } + + total_size += size; + + if (pea->Flag & NeedFlag) { + need_count++; + } + } + + if (!i) { + return 0; + } + + if (total_size != eahdr->TotalSize) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_EASET_SIZE); + Message->Display("%d", clus); + eahdr->TotalSize = total_size; + need_write = TRUE; + } + + if (need_count != eahdr->NeedCount) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_EASET_NEED_COUNT); + Message->Display("%d", clus); + eahdr->NeedCount = need_count; + need_write = TRUE; + } + + EaInfo->OwnHandle = eahdr->OwnHandle; + EaInfo->PreceedingCn = PreceedingCluster; + EaInfo->LastCn = _fat->QueryNthCluster(PreceedingCluster, length); + memcpy(EaInfo->OwnerFileName, eahdr->OwnerFileName, 14); + EaInfo->UsedCount = 0; + + if (need_write) { + if (FixLevel != CheckOnly && !easet.Write()) { + return 0; + } + } + + return EaInfo->LastCn; +} + + +BOOLEAN +FAT_SA::EaSort( + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine sorts the EaInfos array by 'OwnHandle' into ascending order. + It also edits the FAT with the changes in the EAs order. + +Arguments: + + EaInfos - Supplies the array of EA_INFOs to sort. + NumEas - Supplies the number of elements in the array. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not errors have occurred + under check only conditions. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + BOOLEAN done; + EA_INFO tmp; + USHORT clus; + LONG i; + BOOLEAN change; + + done = FALSE; + change = FALSE; + while (!done) { + done = TRUE; + for (i = 0; i < NumEas - 1; i++) { + if (EaInfos[i].OwnHandle > EaInfos[i + 1].OwnHandle) { + done = FALSE; + + clus = _fat->RemoveChain(EaInfos[i + 1].PreceedingCn, + EaInfos[i + 1].LastCn); + + _fat->InsertChain(clus, + EaInfos[i + 1].LastCn, + EaInfos[i].PreceedingCn); + + EaInfos[i + 1].PreceedingCn = EaInfos[i].PreceedingCn; + EaInfos[i].PreceedingCn = EaInfos[i + 1].LastCn; + if (i + 2 < NumEas) { + EaInfos[i + 2].PreceedingCn = EaInfos[i].LastCn; + } + + change = TRUE; + + tmp = EaInfos[i]; + EaInfos[i] = EaInfos[i + 1]; + EaInfos[i + 1] = tmp; + } + } + } + + if (change) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_UNORDERED_EA_SETS); + Message->Display(""); + } + + return TRUE; +} + + +BOOLEAN +FAT_SA::RebuildEaHeader( + IN OUT PUSHORT StartingCluster, + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PMEM EaHeaderMem, + OUT PEA_HEADER EaHeader, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine rebuilds the header and tables of the EA file base on the + information in the 'EaInfos' array. The header log is set to zero, + and the header itself is relocated if any of the clusters are bad. + + The starting cluster may be relocated if there are bad clusters. + +Arguments: + + StartingCluster - Supplies the first cluster of the EA file. + EaInfos - Supplies an array containing information for every + EA set. + NumberOfEas - Supplies the total number of EA sets. + EaHeaderMem - Supplies the memory for the EA header. + EaHeader - Returns the EA header. + FatBitMap - Supplies the cross-links bitmap. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not errors have occurred + under check only conditions. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + ULONG length; + ULONG cluster_size; + USHORT actual_length; + USHORT new_chain; + USHORT last_cluster; + BOOLEAN changes; + LONG i, j, k; + PEA_MAP_TBL table; + PEA_FILE_HEADER header; + LONG tmp; + BOOLEAN empty_ea_file; + USHORT clus; + + + // Compute the number of clusters necessary for the header portion of + // the EA file. + + length = sizeof(EA_FILE_HEADER) + + BaseTableSize*sizeof(USHORT) + + EaInfos[NumEas - 1].OwnHandle*sizeof(USHORT); + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + + if (length%cluster_size) { + length = length/cluster_size + 1; + } else { + length = length/cluster_size; + } + + + // Make sure that the header contains enough clusters to accomodate + // the size of the offset table. + + last_cluster = EaInfos[0].PreceedingCn; + + actual_length = _fat->QueryLengthOfChain(*StartingCluster, last_cluster); + + if (length > actual_length) { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_NEED_MORE_HEADER_SPACE); + Message->Display(""); + + new_chain = _fat->AllocChain((USHORT) (length - actual_length), + &last_cluster); + if (!new_chain) { + Message->Set(MSG_CHK_INSUFFICIENT_DISK_SPACE); + Message->Display(""); + return FALSE; + } + + if (IsCompressed() && !AllocSectorsForChain(new_chain)) { + _fat->FreeChain(new_chain); + Message->Set(MSG_CHK_INSUFFICIENT_DISK_SPACE); + Message->Display(""); + return FALSE; + } + + for (clus = new_chain; + !_fat->IsEndOfChain(clus); + clus = _fat->QueryEntry(clus)) { + + FatBitMap->SetBit(clus); + } + FatBitMap->SetBit(clus); + + _fat->InsertChain(new_chain, last_cluster, EaInfos[0].PreceedingCn); + + EaInfos[0].PreceedingCn = last_cluster; + + } else if (length < actual_length) { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_UNUSED_EA_PORTION); + Message->Display(""); + + last_cluster = _fat->QueryNthCluster(*StartingCluster, + (USHORT) length - 1); + + clus = _fat->RemoveChain(last_cluster, EaInfos[0].PreceedingCn); + + EaInfos[0].PreceedingCn = last_cluster; + + for (; + !_fat->IsEndOfChain(clus); + clus = _fat->QueryEntry(clus)) { + + FatBitMap->ResetBit(clus); + } + FatBitMap->ResetBit(clus); + + } + + + // Verify the cluster chain containing the header. + + changes = FALSE; + if (FixLevel != CheckOnly && + !RecoverChain(StartingCluster, &changes, last_cluster, TRUE)) { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_INSUFFICIENT_DISK_SPACE); + Message->Display(""); + + return FALSE; + } + + if (changes) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_RELOCATED_EA_HEADER); + Message->Display(""); + } + + + // Compute the tables. + + if (!EaHeader->Initialize(EaHeaderMem, _drive, this, _fat, + *StartingCluster, (USHORT) length) || + !(table = EaHeader->GetMapTable()) || + !(header = EaHeader->GetEaFileHeader())) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!EaHeader->Read()) { + if (FixLevel == CheckOnly) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_RELOCATED_EA_HEADER); + Message->Display(""); + } else { + return FALSE; + } + } + + + // Set the log in the header to zero. + + header->Signature = HeaderSignature; + header->FormatType = 0; + header->LogType = 0; + header->Cluster1 = 0; + header->NewCValue1 = 0; + header->Cluster2 = 0; + header->NewCValue2 = 0; + header->Cluster3 = 0; + header->NewCValue3 = 0; + header->Handle = 0; + header->NewHOffset = 0; + + + // Reconcile the tables with the EaInfo information. + + changes = FALSE; + + for (i = 0; i < BaseTableSize; i++) { + table->BaseTab[i] = 0; + } + + j = 0; + empty_ea_file = TRUE; + for (i = 0; i < (LONG) NumEas; i++) { + + if (EaInfos[i].UsedCount != 1) { + continue; + } + + empty_ea_file = FALSE; + + for (; j < (LONG) EaInfos[i].OwnHandle; j++) { + if (table->OffTab[j] != InvalidHandle) { + table->OffTab[j] = InvalidHandle; + changes = TRUE; + } + } + + length = _fat->QueryLengthOfChain(*StartingCluster, + EaInfos[i].PreceedingCn); + + for (k = j>>7; k >= 0 && !table->BaseTab[k]; k--) { + table->BaseTab[k] = (USHORT) length; + } + + tmp = length - table->BaseTab[j>>7]; + + if ((LONG)table->OffTab[j] != tmp) { + table->OffTab[j] = (USHORT) tmp; + changes = TRUE; + } + + j++; + } + + if (empty_ea_file) { + + for (clus = *StartingCluster; + !_fat->IsEndOfChain(clus); + clus = _fat->QueryEntry(clus)) { + + FatBitMap->ResetBit(clus); + + } + FatBitMap->ResetBit(clus); + + *StartingCluster = 0; + + return TRUE; + } + + tmp = _fat->QueryLengthOfChain(*StartingCluster); + for (k = ((j - 1)>>7) + 1; k < BaseTableSize; k++) { + table->BaseTab[k] = (USHORT) tmp; + } + + for (; j < (LONG) EaHeader->QueryOffTabSize(); j++) { + if (table->OffTab[j] != InvalidHandle) { + table->OffTab[j] = InvalidHandle; + changes = TRUE; + } + } + + if (changes) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERROR_IN_EA_HEADER); + Message->Display(""); + } + + return TRUE; +} + + +VOID +FreeSpaceInBitmap( + IN USHORT StartingCluster, + IN PCFAT Fat, + IN OUT PBITVECTOR FatBitMap + ) +{ + if (!StartingCluster) { + return; + } + + while (!Fat->IsEndOfChain(StartingCluster)) { + FatBitMap->ResetBit(StartingCluster); + StartingCluster = Fat->QueryEntry(StartingCluster); + } + FatBitMap->ResetBit(StartingCluster); +} + + +ULONG +ComputeFileNameHashValue( + IN PVOID FileName + ) +{ + ULONG i; + ULONG h; + PUCHAR p; + + p = (PUCHAR) FileName; + h = 0; + for (i = 0; i < 11; i++) { + h += p[i]; + } + + return h; +} + + +BOOLEAN +FAT_SA::WalkDirectoryTree( + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PBITVECTOR FatBitMap, + OUT PFATCHK_REPORT Report, + IN FIX_LEVEL FixLevel, + IN BOOLEAN RecoverAlloc, + IN OUT PMESSAGE Message, + IN BOOLEAN Verbose, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine walks all of the files on the volume by traversing + the directory tree. In doing so it validates all of the + directory entries on the disk. It also verifies the proper + chaining of all file cluster chains. This routine also validates + the integrity of the EA handles for all of the directory entries + on the disk. + + The FatBitMap is used to find and eliminate cross-links in the file + system. + +Arguments: + + EaInfos - Supplies the EA information. + NumEas - Supplies the number of EA sets. + FatBitMap - Supplies a bit map marking all of the clusters + currently in use. + Report - Returns a FAT CHKDSK report on the files of the disk. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + Verbose - Supplies whether or not to be verbose. + NeedErrorsMessage - Supplies whether or not errors have occurred + under check only conditions. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + INTSTACK dirs_to_visit; + INTSTACK paths_of_dirs_tv; + USHORT current_dir; + PFATDIR dir; + FILEDIR filedir; + FAT_DIRENT dirent; + ULONG i, j; + USHORT clus, next; + DSTRING file_path; + PWSTRING new_path; + PWSTRING current_path; + USHORT new_dir; + DSTRING filename; + DSTRING long_name; + HMEM hmem; + CLUSTER_CHAIN cluster; + USHORT new_chain; + ULONG cluster_size; + USHORT length; + DSTRING backslash; + DSTRING eafilename; + DSTRING tmp_string; + BOOLEAN cross_link_detected; + USHORT cross_link_prevclus; + HMEM tmphmem; + FILEDIR tmpfiledir; + FAT_DIRENT tmpdirent1; + FAT_DIRENT tmpdirent2; + BOOLEAN non_zero_dirents; + BITVECTOR file_name_hash_table; + CONST hash_table_size = 256*11; + ULONG hash_value; + BOOLEAN has_long_entry = FALSE; + UCHAR chksum; + BOOLEAN broke; + ULONG first_long_entry; + FAT_DIRENT dirent2; + ULONG allocated_clusters; + ULONG percent, new_percent; + + DebugAssert(sizeof(PUCHAR) <= sizeof(INT)); + DebugAssert(sizeof(USHORT) <= sizeof(INT)); + + memset(Report, 0, sizeof(FATCHK_REPORT)); + Report->ExitStatus = CHKDSK_EXIT_SUCCESS; + + if (!dirs_to_visit.Initialize() || + !paths_of_dirs_tv.Initialize() || + !file_name_hash_table.Initialize(hash_table_size)) { + return FALSE; + } + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + allocated_clusters = _fat->QueryAllocatedClusters(); + + if (0 == allocated_clusters) { + allocated_clusters++; + } + + if (!backslash.Initialize("\\") || + !eafilename.Initialize("EA DATA. SF")) { + return FALSE; + } + + if (!(current_path = NEW DSTRING) || + !current_path->Initialize(&backslash)) { + return FALSE; + } + + if (!dirs_to_visit.Push(0) || + !paths_of_dirs_tv.Push((INT) current_path)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + Message->Set(MSG_CHK_CHECKING_FILES); + Message->Display(""); + + percent = 0; + Message->Set(MSG_PERCENT_COMPLETE); + if (!Message->Display("%d", percent)) { + return FALSE; + } + + for (; dirs_to_visit.QuerySize(); DELETE( current_path )) { + + current_dir = (USHORT) dirs_to_visit.Look().GetLowPart(); + current_path = (PWSTRING) paths_of_dirs_tv.Look().GetLowPart(); + dirs_to_visit.Pop(); + paths_of_dirs_tv.Pop(); + has_long_entry = FALSE; + + if (current_dir) { + if (!hmem.Initialize() || + !filedir.Initialize(&hmem, _drive, this, _fat, current_dir)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!filedir.Read()) { + Message->Set(MSG_BAD_DIR_READ); + Message->Display(""); + return FALSE; + } + + dir = &filedir; + } else { + dir = _dir; + } + + file_name_hash_table.ResetAll(); + + for (i = (current_dir ? 2 : 0); ; i++) { + + if (!dirent.Initialize(dir->GetDirEntry(i)) || + dirent.IsEndOfDirectory()) { + + if (has_long_entry) { + // + // There was an orphaned lfn at the end of the + // directory. Erase it now. + // + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_BAD_LONG_NAME); + Message->Display( "%W", current_path ); + + EraseAssociatedLongName(dir, first_long_entry, i); + + has_long_entry = FALSE; + } + + // This code must make sure that all other directory + // entries are end of directory entries. + + non_zero_dirents = FALSE; + + for (; dirent.Initialize(dir->GetDirEntry(i)); i++) { + + if (!dirent.IsEndOfDirectory()) { + non_zero_dirents = TRUE; + dirent.SetEndOfDirectory(); + } + } + + if (non_zero_dirents) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_TRAILING_DIRENTS); + Message->Display("%W", current_path); + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + break; + } + + if (dirent.IsErased()) { + + if (has_long_entry) { + + // + // The preceding lfn is orphaned. Remove it. + // + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_BAD_LONG_NAME); + Message->Display( "%W", current_path ); + + EraseAssociatedLongName(dir, first_long_entry, i); + + has_long_entry = FALSE; + } + + continue; + } + + if (dirent.IsLongEntry()) { + + // skip long name entries; come back to them later + + if (has_long_entry) { + // already amid long entry + continue; + } + + // first long entry + + has_long_entry = TRUE; + first_long_entry = i; + continue; + } + + dirent.QueryName(&filename); + + if (has_long_entry) { + + DSTRING lfn; + + // + // The current entry is short, and we've just finished + // skipping the associated long entry. Look back through + // the long entries, make sure they're okay. + // + + broke = FALSE; + + chksum = dirent.QueryChecksum(); + + for (j = i - 1; j >= first_long_entry; j--) { + dirent2.Initialize(dir->GetDirEntry(j)); + + if (!dirent2.IsLongNameEntry()) { + continue; + } + + broke = (dirent2.QueryLongOrdinal() != i - j) || + (dirent2.QueryChecksum() != chksum) || + (dirent2.QueryStartingCluster() != 0); + + broke = broke || !dirent2.IsWellTerminatedLongNameEntry(); + + if (broke || dirent2.IsLastLongEntry()) { + break; + } + } + + broke = broke || (!dirent2.IsLastLongEntry()); + +#if 0 +//MJB: We'll elide this code because Win95 isn't this strict and we +// don't want to delete all their lfn's. + + if (!broke && dir->QueryLongName(i, &lfn)) { + + broke = !dirent.NameHasTilde() && + (dirent.NameHasExtendedChars() || + 0 != filename.Stricmp(&lfn)) && + !IsString8Dot3(&lfn); + } +#endif + + if (broke) { + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + // + // Erase all the long name entries. + // + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_BAD_LONG_NAME); + Message->Display( "%W", current_path ); + + EraseAssociatedLongName(dir, first_long_entry, i); + + has_long_entry = FALSE; + + } + + // + // Fall into code to check short name. + // + } + + dirent.QueryName(&filename); + + if (!file_path.Initialize(current_path)) { + return FALSE; + } + + if (current_dir) { + if (!file_path.Strcat(&backslash)) { + return FALSE; + } + } + + if (dir->QueryLongName(i, &long_name) && + long_name.QueryChCount() != 0) { + + if (!file_path.Strcat(&long_name)) { + return FALSE; + } + + } else { + + if (!file_path.Strcat(&filename)) { + return FALSE; + } + } + + if (Verbose && !dirent.IsVolumeLabel()) { + Message->Set(MSG_CHK_FILENAME); + Message->Display("%W", &file_path); + } + + if (!ValidateDirent(&dirent, &file_path, FixLevel, RecoverAlloc, + Message, NeedErrorsMessage, FatBitMap, + &cross_link_detected, &cross_link_prevclus, + &Report->ExitStatus)) { + return FALSE; + } + + if (dirent.IsErased()) { + + // + // ValidateDirent erased this entry, presumably because it's + // hosed. Remove corresponding long name, if any. + // + + if (has_long_entry) { + EraseAssociatedLongName(dir, first_long_entry, i); + has_long_entry = FALSE; + } + continue; + } + + // + // Analize for cross-links. + // + + if (cross_link_detected) { // CROSSLINK !! + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + // Identify cross linked cluster. + + clus = cross_link_prevclus; + + next = cross_link_prevclus ? + _fat->QueryEntry(cross_link_prevclus) : + dirent.QueryStartingCluster(); + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CROSS_LINK); + Message->Display("%W%d", &file_path, next); + + if (dirent.IsDirectory()) { + + Message->Set(MSG_CHK_DIR_TRUNC); + Message->Display(""); + + if (clus) { + _fat->SetEndOfChain(clus); + } else { + dirent.SetErased(); + if (has_long_entry) { + EraseAssociatedLongName(dir, first_long_entry, i); + has_long_entry = FALSE; + } + continue; + } + + } else { + + if (!CopyClusters(next, &new_chain, FatBitMap, + FixLevel, Message)) { + return FALSE; + } + + if (new_chain) { + Message->Set(MSG_CHK_CROSS_LINK_COPY); + Message->Display(""); + + if (clus) { + _fat->SetEntry(clus, new_chain); + } else { + dirent.SetStartingCluster(new_chain); + } + + } else { + + Message->Set(MSG_CHK_CROSS_LINK_TRUNC); + Message->Display(""); + + if (clus) { + + _fat->SetEndOfChain(clus); + dirent.SetFileSize( + cluster_size*_fat->QueryLengthOfChain( + dirent.QueryStartingCluster())); + + } else { + dirent.SetErased(); + + if (has_long_entry) { + EraseAssociatedLongName(dir, first_long_entry, + i); + has_long_entry = FALSE; + } + } + } + } + } + + + if (!ValidateEaHandle(&dirent, current_dir, i, EaInfos, NumEas, + &file_path, FixLevel, Message, + NeedErrorsMessage)) { + return FALSE; + } + + + if (!dirent.IsVolumeLabel()) { + + // Make sure that the filename is unique. + + hash_value = ComputeFileNameHashValue(dir->GetDirEntry(i))% + hash_table_size; + + if (file_name_hash_table.IsBitSet(hash_value)) { + + // search the directory since the hash values match. + + for (j = 0; j < i; j++) { + + FAT_DIRENT fd; + + if (!fd.Initialize(dir->GetDirEntry(j)) || + fd.IsVolumeLabel()) { + continue; + } + + if (!memcmp(dir->GetDirEntry(j), dir->GetDirEntry(i), 11)) { + + break; + } + } + + if (j < i) { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_REPEATED_ENTRY); + Message->Display("%W%W", &filename, current_path); + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + FreeSpaceInBitmap(dirent.QueryStartingCluster(), _fat, + FatBitMap); + + dirent.SetErased(); + + if (has_long_entry) { + EraseAssociatedLongName(dir, first_long_entry, i); + has_long_entry = FALSE; + } + continue; + } + + } else { + file_name_hash_table.SetBit(hash_value); + } + } + + + // + // Do special stuff if the current entry is a directory. + // + + if (dirent.IsDirectory()) { + + new_dir = dirent.QueryStartingCluster(); + + // + // Validate the integrity of the directory. + // + + // Read the directory. + + if (!tmphmem.Initialize() || + !tmpfiledir.Initialize(&tmphmem, _drive, this, _fat, + new_dir) || + !tmpfiledir.Read()) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + + return FALSE; + } + + // Check the . and .. entries. + + if (!tmpdirent1.Initialize(tmpfiledir.GetDirEntry(0)) || + !tmpdirent2.Initialize(tmpfiledir.GetDirEntry(1))) { + DebugAbort("GetDirEntry of 0 and 1 failed!"); + return FALSE; + } + + if (!tmpdirent1.IsDot() || + !tmpdirent2.IsDotDot() || + !tmpdirent1.IsDirectory() || + !tmpdirent2.IsDirectory()) { + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERROR_IN_DIR); + Message->Display("%W", &file_path); + Message->Set(MSG_CHK_CONVERT_DIR_TO_FILE, NORMAL_MESSAGE, TEXT_MESSAGE); + Message->Display(); + + if (Message->IsYesResponse(TRUE)) { + dirent.ResetDirectory(); + dirent.SetFileSize( + _fat->QueryLengthOfChain(new_dir)* + cluster_size); + + } else { + + FreeSpaceInBitmap(dirent.QueryStartingCluster(), + _fat, FatBitMap); + + dirent.SetErased(); + + if (has_long_entry) { + EraseAssociatedLongName(dir, first_long_entry, i); + has_long_entry = FALSE; + } + continue; + } + + } else { // Directory looks valid. + + if (tmpdirent1.QueryStartingCluster() != new_dir || + tmpdirent2.QueryStartingCluster() != current_dir || + tmpdirent1.QueryFileSize() || + tmpdirent2.QueryFileSize()) { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_ERRORS_IN_DIR_CORR); + Message->Display("%W", &file_path); + + Report->ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + + tmpdirent1.SetStartingCluster(new_dir); + tmpdirent2.SetStartingCluster(current_dir); + tmpdirent1.SetFileSize(0); + tmpdirent2.SetFileSize(0); + + if (FixLevel != CheckOnly && !tmpfiledir.Write()) { + DebugAbort("Could not write tmp file dir."); + return FALSE; + } + } + + // Add the directory to the list of directories + // to validate. + + if (!(new_path = NEW DSTRING) || + !new_path->Initialize(&file_path)) { + return FALSE; + } + + if (!dirs_to_visit.Push((ULONG) new_dir) || + !paths_of_dirs_tv.Push((ULONG) new_path)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + } + } + + + // + // Generate report stats. + // + + if (current_dir || !(filename == eafilename)) { + length = _fat->QueryLengthOfChain(dirent.QueryStartingCluster()); + if (dirent.IsHidden()) { + Report->HiddenEntriesCount++; + Report->HiddenClusters += length; + } else if (dirent.IsDirectory()) { + Report->DirEntriesCount++; + Report->DirClusters += length; + } else if (!dirent.IsVolumeLabel()) { + Report->FileEntriesCount++; + Report->FileClusters += length; + } + } + + new_percent = (Report->HiddenClusters + Report->DirClusters + + Report->FileClusters) * 100 / allocated_clusters; + + if (new_percent > percent) { + percent = new_percent; + Message->Set(MSG_PERCENT_COMPLETE); + if (!Message->Display("%d", percent)) { + return FALSE; + } + } + + has_long_entry = FALSE; + } + + if (current_dir) { + if (FixLevel != CheckOnly && !filedir.Write()) { + return FALSE; + } + } + } + + Message->Set(MSG_PERCENT_COMPLETE); + if (!Message->Display("%d", 100)) { + return FALSE; + } + Message->Set(MSG_CHK_DONE_CHECKING); + Message->Display(""); + + return TRUE; +} + + +BOOLEAN +FAT_SA::ValidateDirent( + IN OUT PFAT_DIRENT Dirent, + IN PCWSTRING FilePath, + IN FIX_LEVEL FixLevel, + IN BOOLEAN RecoverAlloc, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage, + IN OUT PBITVECTOR FatBitMap, + OUT PBOOLEAN CrossLinkDetected, + OUT PUSHORT CrossLinkPreviousCluster, + OUT PULONG ExitStatus + ) +/*++ + +Routine Description: + + This routine verifies that all components of a directory entry are + correct. If the time stamps are invalid then they will be corrected + to the current time. If the filename is invalid then the directory + entry will be marked as deleted. If the cluster number is out of + disk range then the directory entry will be marked as deleted. + Otherwise, the cluster chain will be validated and the length of + the cluster chain will be compared against the file size. If there + is a difference then the file size will be corrected. + + If there are any strange errors then FALSE will be returned. + +Arguments: + + Dirent - Supplies the directory entry to validate. + FilePath - Supplies the full path name for the directory + entry. + RecoverAlloc - Supplies whether or not to recover all + allocated space on the volume. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has + occurred during check only mode. + FatBitMap - Supplies a bitmap marking in use all known + clusters. + CrossLinkDetected - Returns TRUE if the file is cross-linked with + another. + CrossLinkPreviousCluster - Returns the cluster previous to the + cross-linked one. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + USHORT start_clus; + BOOLEAN changes; + USHORT length; + ULONG file_size; + ULONG cluster_size; + BOOLEAN recover_status; + + DebugAssert(CrossLinkDetected); + DebugAssert(CrossLinkPreviousCluster); + + *CrossLinkDetected = FALSE; + + if (Dirent->IsErased()) { + return TRUE; + } + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + +// Don't validate names or time stamps anymore. +#if 0 + + if (!Dirent->IsValidName()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_INVALID_NAME); + Message->Display("%W", FilePath); + Dirent->SetErased(); + return TRUE; + } + + if (!Dirent->IsValidTimeStamp()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_INVALID_TIME_STAMP); + Message->Display("%W", FilePath); + if (!Dirent->SetTimeStamp()) { + return FALSE; + } + } + +#endif + + if (Dirent->IsDirectory() && Dirent->QueryFileSize()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_DIR_HAS_FILESIZE); + Message->Display("%W", FilePath); + Dirent->SetFileSize( 0 ); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + if ((start_clus = Dirent->QueryStartingCluster()) != 0 ) { + if (!_fat->IsInRange(start_clus) || _fat->IsClusterFree(start_clus)) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_FIRST_UNIT); + Message->Display("%W", FilePath); + Dirent->SetErased(); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + return TRUE; + } + + if (Dirent->IsDirectory() || RecoverAlloc) { + _fat->ScrubChain(start_clus, &changes); + + if (changes) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_LINK); + Message->Display("%W", FilePath); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + // Validate the readability of the directory or file + // in the case that 'RecoverAlloc' is TRUE. + + if (Dirent->IsDirectory()) { + if (!(recover_status = RecoverChain(&start_clus, &changes))) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + } else if (FixLevel != CheckOnly) { + recover_status = RecoverChain(&start_clus, &changes, 0, TRUE); + } else { + recover_status = TRUE; + changes = FALSE; + } + + if (changes) { + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + dofmsg(Message, NeedErrorsMessage); + if (Dirent->IsDirectory()) { + if (!start_clus) { + Message->Set(MSG_CHK_BAD_DIR); + Message->Display("%W", FilePath); + Dirent->SetErased(); + return TRUE; + } else { + Message->Set(MSG_CHK_BAD_CLUSTERS_IN_DIR); + Message->Display("%W", FilePath); + Dirent->SetStartingCluster(start_clus); + } + } else { + // In the file case, since we're replacing bad clusters + // with new ones, start_clus cannot be zero. + DebugAssert(start_clus); + + if (recover_status) { + Message->Set(MSG_CHK_BAD_CLUSTERS_IN_FILE_SUCCESS); + Message->Display("%W", FilePath); + } else { + Message->Set(MSG_CHK_BAD_CLUSTERS_IN_FILE_FAILURE); + Message->Display(); + } + Dirent->SetStartingCluster(start_clus); + } + } + } + + _fat->ScrubChain(start_clus, FatBitMap, &changes, + CrossLinkDetected, CrossLinkPreviousCluster); + + if (changes) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_LINK); + Message->Display("%W", FilePath); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + + length = _fat->QueryLengthOfChain(start_clus); + + if (( file_size = Dirent->QueryFileSize()) != 0 ) { + if (file_size <= ((USHORT)(length - 1))*cluster_size || + file_size > length*cluster_size) { + + // Note that no message is displayed if the + // file size is less than the allocation--it + // is just silently corrected. + // + if (file_size > length*cluster_size) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_FILE_SIZE); + Message->Display("%W", FilePath); + } + Dirent->SetFileSize(length*cluster_size); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + } else { + if (!Dirent->IsDirectory()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_FILE_SIZE); + Message->Display("%W", FilePath); + Dirent->SetFileSize(length*cluster_size); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + } + } else { + if (Dirent->IsDirectory() && !Dirent->IsDotDot()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_LINK); + Message->Display("%W", FilePath); + Dirent->SetErased(); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + return TRUE; + } + + if (Dirent->QueryFileSize()) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_BAD_FILE_SIZE); + Message->Display("%W", FilePath); + Dirent->SetFileSize(0); + *ExitStatus = CHKDSK_EXIT_ERRS_FIXED; + } + } + + return TRUE; +} + + +BOOLEAN +FAT_SA::ValidateEaHandle( + IN OUT PFAT_DIRENT Dirent, + IN USHORT DirClusterNumber, + IN ULONG DirEntryNumber, + IN OUT PEA_INFO EaInfos, + IN USHORT NumEas, + IN PCWSTRING FilePath, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine validates the EA handle in the directory entry 'Dirent'. + It ensures that it references an actual EA set. It also ensures + that it is the only directory entry which references the EA set. + + If several entries try to reference the same EA set then ties will + be broken based on the 'OwnerFileName' entry in the EA set. + +Arguments: + + Dirent - Supplies the directory entry to validate. + DirClusterNumber - Supplies the cluster number of the directory + containing the dirent. + DirEntryNumber - Supplies the position of the directory entry in + the directory. + EaInfos - Supplies the list of current EA information. + NumEas - Supplies the number of EA sets. + FilePath - Supplies the full path name for the directory entry. + FixLevel - Supplies the fix up level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has occurred + during check only mode. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + USHORT i; + USHORT handle; + DSTRING wfilename; + STR filename[14]; + BOOLEAN remove_other_handle; + HMEM hmem; + FILEDIR filedir; + FAT_DIRENT other_dirent; + + + if (!(handle = Dirent->QueryEaHandle())) { + return TRUE; + } + + if (!EaInfos) { + NumEas = 0; + } + + for (i = 0; i < NumEas; i++) { + if (handle == EaInfos[i].OwnHandle) { + break; + } + } + + if (i == NumEas) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_UNRECOG_EA_HANDLE); + Message->Display("%W", FilePath); + Dirent->SetEaHandle(0); + return TRUE; + } + + if (EaInfos[i].UsedCount >= 2) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_SHARED_EA); + Message->Display("%W", FilePath); + Dirent->SetEaHandle(0); + return TRUE; + } + + Dirent->QueryName(&wfilename); + if (!wfilename.QuerySTR( 0, TO_END, filename, 14)) { + return FALSE; + } + + if (EaInfos[i].UsedCount == 0) { + memcpy(EaInfos[i].UserFileName, filename, 14); + EaInfos[i].UserFileEntryCn = DirClusterNumber; + EaInfos[i].UserFileEntryNumber = DirEntryNumber; + EaInfos[i].UsedCount = 1; + return TRUE; + } + + + // UsedCount == 1. + + remove_other_handle = FALSE; + + if (!strcmp(filename, EaInfos[i].OwnerFileName)) { + + remove_other_handle = TRUE; + + if (!strcmp(EaInfos[i].UserFileName, + EaInfos[i].OwnerFileName)) { + EaInfos[i].UsedCount = 2; + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_SHARED_EA); + Message->Display("%W", FilePath); + Dirent->SetEaHandle(0); + } + + } else { + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_SHARED_EA); + Message->Display("%W", FilePath); + Dirent->SetEaHandle(0); + + if (strcmp(EaInfos[i].UserFileName, + EaInfos[i].OwnerFileName)) { + EaInfos[i].UsedCount = 2; + remove_other_handle = TRUE; + } + } + + + if (remove_other_handle) { + + if (EaInfos[i].UserFileEntryCn) { + if (!hmem.Initialize() || + !filedir.Initialize(&hmem, _drive, this, _fat, + EaInfos[i].UserFileEntryCn) || + !filedir.Read() || + !other_dirent.Initialize(filedir.GetDirEntry( + EaInfos[i].UserFileEntryNumber))) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + } else { + if (!other_dirent.Initialize(_dir->GetDirEntry( + EaInfos[i].UserFileEntryNumber))) { + return FALSE; + } + } + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_SHARED_EA); + Message->Display("%W", FilePath); + other_dirent.SetEaHandle(0); + + if (EaInfos[i].UserFileEntryCn && FixLevel != CheckOnly && + !filedir.Write()) { + return FALSE; + } + + strcpy(EaInfos[i].UserFileName, filename); + EaInfos[i].UserFileEntryCn = DirClusterNumber; + EaInfos[i].UserFileEntryNumber = DirEntryNumber; + } + + return TRUE; +} + + +BOOLEAN +FAT_SA::CopyClusters( + IN USHORT SourceChain, + OUT PUSHORT DestChain, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine copies the cluster chain beginning at 'SourceChain' + to a free portion of the disk. The beginning of the copied chain + will be returned in 'DestChain'. If there isn't enough free space + on the disk to copy the chain then 'DestChain' will return 0. + +Arguments: + + SourceChain - Supplies the chain to copy. + DestChain - Returns the copy of the chain. + FatBitMap - Supplies the orphan and cross-link bitmap. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + HMEM hmem; + CLUSTER_CHAIN cluster; + USHORT src, dst; + BOOLEAN changes; + USHORT clus; + + if (!hmem.Initialize()) { + return FALSE; + } + + if (!(*DestChain = _fat->AllocChain( + _fat->QueryLengthOfChain(SourceChain)))) { + return TRUE; + } + + changes = FALSE; + if (FixLevel != CheckOnly && !RecoverChain(DestChain, &changes, 0, TRUE)) { + if (*DestChain) { + _fat->FreeChain(*DestChain); + } + *DestChain = 0; + return TRUE; + } + + if (IsCompressed() && !AllocSectorsForChain(*DestChain)) { + _fat->FreeChain(*DestChain); + *DestChain = 0; + return TRUE; + } + + // Mark the new chain as "used" in the FAT bitmap. + for (clus = *DestChain; + !_fat->IsEndOfChain(clus); + clus = _fat->QueryEntry(clus)) { + + FatBitMap->SetBit(clus); + } + FatBitMap->SetBit(clus); + + src = SourceChain; + dst = *DestChain; + for (;;) { + if (!cluster.Initialize(&hmem, _drive, this, _fat, src, 1)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + cluster.Read(); + + if (!cluster.Initialize(&hmem, _drive, this, _fat, dst, 1)) { + return FALSE; + } + + if (FixLevel != CheckOnly && !cluster.Write()) { + return FALSE; + } + + if (_fat->IsEndOfChain(src)) { + break; + } + + src = _fat->QueryEntry(src); + dst = _fat->QueryEntry(dst); + } + + return TRUE; +} + + +BOOLEAN +FAT_SA::PurgeEaFile( + IN PCEA_INFO EaInfos, + IN USHORT NumEas, + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine is executed after the directory tree is walked. Stored, + in the EaInfos array, is information concerning which EAs get used + and by how many files. + + If an EA set is not used, or is used by more than one file, then this + routine will eliminate it from the EA file. + +Arguments: + + EaInfos - Supplies an array of EA information. + NumEas - Supplies the number of EA sets. + FatBitMap - Supplies the FAT cross-link detection bitmap. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has occured + in check only mode. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + LONG i; + EA_SET easet; + HMEM hmem; + PEA_HDR eahdr; + USHORT clus; + + if (!hmem.Initialize()) { + return FALSE; + } + + for (i = NumEas - 1; i >= 0; i--) { + + if (EaInfos[i].UsedCount != 1) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_UNUSED_EA_SET); + Message->Display("%d", EaInfos[i].OwnHandle); + + // Mark the FAT entries of the removed chain as "not claimed", + // for the purposes of orphan recovery. + + for (clus = _fat->QueryEntry(EaInfos[i].PreceedingCn); + clus != EaInfos[i].LastCn; + clus = _fat->QueryEntry(clus)) { + + FatBitMap->ResetBit(clus); + + } + FatBitMap->ResetBit(clus); + + + // Remove the unused EA chain from the EA file. + + _fat->RemoveChain(EaInfos[i].PreceedingCn, + EaInfos[i].LastCn); + + + } else if (strcmp(EaInfos[i].OwnerFileName, + EaInfos[i].UserFileName)) { + + if (!easet.Initialize(&hmem, _drive, this, _fat, + _fat->QueryEntry(EaInfos[i].PreceedingCn), + 1) || + !easet.Read() || + !(eahdr = easet.GetEaSetHeader())) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_NEW_OWNER_NAME); + Message->Display("%d%s%s", EaInfos[i].OwnHandle, + eahdr->OwnerFileName, EaInfos[i].UserFileName); + + memcpy(eahdr->OwnerFileName, EaInfos[i].UserFileName, 14); + + if (FixLevel != CheckOnly && !easet.Write()) { + return FALSE; + } + } + } + + return TRUE; +} + +BOOLEAN +FAT_SA::RecoverOrphans( + IN OUT PBITVECTOR FatBitMap, + IN FIX_LEVEL FixLevel, + IN OUT PMESSAGE Message, + IN OUT PBOOLEAN NeedErrorsMessage + ) +/*++ + +Routine Description: + + This routine examines the file system for cluster chains which are + not claimed by any file. These 'orphans' will then be recovered in + a subdirectory of the root or removed from the system. + +Arguments: + + FatBitMap - Supplies a bit map marking all currently used + clusters. + FixLevel - Supplies the CHKDSK fix level. + Message - Supplies an outlet for messages. + NeedErrorsMessage - Supplies whether or not an error has occured + in check only mode. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _AUTOCHECK_ ) + // Due to memory constraints, the maximum number of orphans to + // recover is less for Autochk than for run-time Chkdsk. + // + CONST maximum_orphans = 1000; +#else + CONST maximum_orphans = 10000; +#endif + + USHORT i; + USHORT clus; + BOOLEAN changes; + HMEM hmem; + FILEDIR found_dir; + STR found_name[14]; + DSTRING wfound_name; + STR filename[14]; + FAT_DIRENT dirent; + USHORT found_cluster; + USHORT orphan_count; + ULONG cluster_size; + USHORT found_length; + USHORT next; + PUCHAR orphan_track; + USHORT cluster_count; + USHORT num_orphans; + USHORT num_orphan_clusters; + DSTRING tmp_string; + BITVECTOR tmp_bitvector; + BOOLEAN tmp_bool; + USHORT tmp_ushort; + + cluster_count = QueryClusterCount(); + + if (!(orphan_track = NEW UCHAR[cluster_count])) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + memset(orphan_track, 0, cluster_count); + + if (!tmp_bitvector.Initialize(cluster_count)) { + return FALSE; + } + + cluster_size = _drive->QuerySectorSize()*QuerySectorsPerCluster(); + + num_orphans = 0; + num_orphan_clusters = 0; + for (i = FirstDiskCluster; _fat->IsInRange(i); i++) { + if (!_fat->IsClusterFree(i) && + !FatBitMap->IsBitSet(i) && + !_fat->IsClusterBad(i) && + !_fat->IsClusterReserved(i)) { + + num_orphans++; + + tmp_bitvector.ResetAll(); + + _fat->ScrubChain(i, &tmp_bitvector, &changes, + &tmp_bool, &tmp_ushort); + + if (changes) { + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_BAD_LINKS_IN_ORPHANS); + Message->Display("%d", i); + } + + num_orphan_clusters++; + + clus = i; + while (!_fat->IsEndOfChain(clus)) { + next = _fat->QueryEntry(clus); + + if (orphan_track[next] == 1) { + num_orphans--; + orphan_track[next] = 2; + break; + } + + if (FatBitMap->IsBitSet(next)) { // CROSSLINK !! + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CHK_CROSS_LINKED_ORPHAN); + Message->Display("%d", clus); + + _fat->SetEndOfChain(clus); + + break; + } + + num_orphan_clusters++; + + FatBitMap->SetBit(next); + orphan_track[next] = 2; + + clus = next; + } + FatBitMap->SetBit(i); + orphan_track[i] = 1; + } + } + + + // Now scan throught the secondary pointers in search of orphans. + + changes = FALSE; + for (i = FirstDiskCluster; _fat->IsInRange(i); i++) { + if (orphan_track[i]) { + changes = TRUE; + break; + } + } + + if (!changes) { + // No orphans to recover. + return TRUE; + } + + dofmsg(Message, NeedErrorsMessage); + Message->Set(MSG_CONVERT_LOST_CHAINS, NORMAL_MESSAGE, TEXT_MESSAGE); + Message->Display(""); + + if (!Message->IsYesResponse(TRUE)) { + + if (FixLevel != CheckOnly) { + + for (i = FirstDiskCluster; _fat->IsInRange(i); i++) { + if (orphan_track[i] == 1) { + _fat->FreeChain(i); + } + } + + } + + Message->Set((FixLevel == CheckOnly) ? + MSG_BYTES_WOULD_BE_FREED : + MSG_BYTES_FREED); + + + Message->Display("%d", cluster_size*num_orphan_clusters); + + return TRUE; + } + + + // Set up for orphan recovery. + + + // Establish "FOUND.XXX" directory. + for (i = 0; i < 1000; i++) { + sprintf(found_name, "FOUND.%03d", i); + if (!wfound_name.Initialize(found_name)) { + return FALSE; + } + + if (!_dir->SearchForDirEntry(&wfound_name)) { + break; + } + } + + if (i == 1000) { + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + found_length = (USHORT)((min(num_orphans,maximum_orphans)*BytesPerDirent - 1)/cluster_size + 1); + + if (!(found_cluster = _fat->AllocChain(found_length)) && + !(found_cluster = _fat->AllocChain(found_length = 1))) { + Message->Set(MSG_ORPHAN_DISK_SPACE); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + // Check the chain. + changes = FALSE; + if (FixLevel != CheckOnly && + !RecoverChain(&found_cluster, &changes, 0, TRUE)) { + Message->Set(MSG_ORPHAN_DISK_SPACE); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + if (!hmem.Initialize() || + !found_dir.Initialize(&hmem, _drive, this, _fat, found_cluster)) { + DebugAbort( "Initialization failed" ); + return FALSE; + } + + // Allocate space for the cluster chain in the sector heap (fat_db) + + if (IsCompressed() && !AllocSectorsForChain(found_cluster)) { + _fat->FreeChain(found_cluster); + Message->Set(MSG_ORPHAN_DISK_SPACE); + Message->Display(""); + + return TRUE; + } + + memset(hmem.GetBuf(), 0, (UINT) hmem.QuerySize()); + + if (!dirent.Initialize(_dir->GetFreeDirEntry())) { + Message->Set(MSG_NO_ROOM_IN_ROOT); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + dirent.Clear(); + + if (!dirent.SetName(&wfound_name)) { + return FALSE; + } + + dirent.SetDirectory(); + + if (!dirent.SetLastWriteTime() || !dirent.SetCreationTime() || + !dirent.SetLastAccessTime()) { + return FALSE; + } + + dirent.SetStartingCluster(found_cluster); + + if (!dirent.Initialize(found_dir.GetDirEntry(0))) { + return FALSE; + } + + dirent.Clear(); + + if (!tmp_string.Initialize(".")) { + return FALSE; + } + + if (!dirent.SetName(&tmp_string)) { + return FALSE; + } + + dirent.SetDirectory(); + + if (!dirent.SetLastWriteTime() || !dirent.SetCreationTime() || + !dirent.SetLastAccessTime()) { + return FALSE; + } + + dirent.SetStartingCluster(found_cluster); + + if (!dirent.Initialize(found_dir.GetDirEntry(1))) { + return FALSE; + } + + dirent.Clear(); + + if (!tmp_string.Initialize("..")) { + return FALSE; + } + + if (!dirent.SetName(&tmp_string)) { + return FALSE; + } + + dirent.SetDirectory(); + + if (!dirent.SetLastWriteTime() || !dirent.SetCreationTime() || + !dirent.SetLastAccessTime()) { + return FALSE; + } + + dirent.SetStartingCluster(0); + + + // OK, now let's recover those orphans. + + orphan_count = 0; + for (i = FirstDiskCluster; _fat->IsInRange(i); i++) { + if (orphan_track[i] != 1) { + continue; + } + if (orphan_count == maximum_orphans) { + Message->Set(MSG_TOO_MANY_ORPHANS); + Message->Display(""); + break; + } + + if (!dirent.Initialize(found_dir.GetFreeDirEntry())) { + + if (_fat->ReAllocChain(found_cluster, ++found_length) + != found_length) { + Message->Set(MSG_ORPHAN_DISK_SPACE); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + break; + } + +//XXX.mjb: FATDB: need to get sectors for found_cluster + realloc. + + changes = FALSE; + if (FixLevel != CheckOnly && + !RecoverChain(&found_cluster, &changes, 0, TRUE)) { + + Message->Set(MSG_ORPHAN_DISK_SPACE); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + if (FixLevel != CheckOnly && !found_dir.Write()) { + return FALSE; + } + + if (!hmem.Initialize() || + !found_dir.Initialize(&hmem, _drive, this, _fat, + found_cluster) || + !found_dir.Read()) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + Message->Set(MSG_WOULD_BE_RECOVERED_FILES); + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + return TRUE; + } + + if (!dirent.Initialize(found_dir.GetDirEntry(2 + orphan_count))) { + return FALSE; + } + + dirent.SetEndOfDirectory(); + + if (!dirent.Initialize(found_dir.GetFreeDirEntry())) { + return FALSE; + } + } + + sprintf(filename, "FILE%04d.CHK", orphan_count); + + if (!tmp_string.Initialize(filename)) { + return FALSE; + } + + dirent.Clear(); + + if (!dirent.SetName(&tmp_string)) { + return FALSE; + } + + if (!dirent.SetLastWriteTime() || !dirent.SetCreationTime() || + !dirent.SetLastAccessTime()) { + return FALSE; + } + + dirent.SetStartingCluster(i); + dirent.SetFileSize(cluster_size*_fat->QueryLengthOfChain(i)); + + orphan_count++; + } + + // Set all dirents past the orphan count to end of directory. + + for (i = 2 + orphan_count; dirent.Initialize(found_dir.GetDirEntry(i)); i++) { + dirent.SetEndOfDirectory(); + } + + if (FixLevel != CheckOnly && !found_dir.Write()) { + return FALSE; + } + + Message->Set((FixLevel == CheckOnly) ? + MSG_WOULD_BE_RECOVERED_FILES : + MSG_RECOVERED_FILES); + + Message->Display("%d%d", cluster_size*num_orphan_clusters, + num_orphans); + + return TRUE; +} + + +BOOLEAN +FAT_SA::AllocSectorsForChain( + ULONG ChainHead + ) +/*++ + +Routine Description: + + When VerifyAndFix needs to allocate a cluster chain in order + to create a new directory (such as \FOUND.000), it also needs to + allocate space in the sector heap for data blocks for those + clusters. This routine does that. + +Arguments: + + ChainHead - a cluster chain; data blocks are allocated for each + cluster in this chain. + +Return Value: + + TRUE - Success. + FALSE - Failure - not enough disk space + +--*/ +{ + USHORT clus; + USHORT next; + + clus = (USHORT)ChainHead; + for (;;) { + if (!AllocateClusterData(clus, + (UCHAR)QuerySectorsPerCluster(), + FALSE, + (UCHAR)QuerySectorsPerCluster())) { + break; + } + + if (_fat->IsEndOfChain(clus)) { + return TRUE; + } + + clus = _fat->QueryEntry(clus); + } + + // Error: not enough disk space. XXX.mjb + + // Free the sectors we already allocated + + while (ChainHead != clus) { + FreeClusterData(ChainHead); + next = _fat->QueryEntry((USHORT)ChainHead); + _fat->SetClusterFree((USHORT)ChainHead); + ChainHead = next; + } + + return FALSE; +} + +#if defined( _SETUP_LOADER_ ) + +BOOLEAN +FAT_SA::RecoverFreeSpace( + IN OUT PMESSAGE Message + ) +{ + return TRUE; +} + +#else // _SETUP_LOADER_ not defined + +BOOLEAN +FAT_SA::RecoverFreeSpace( + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine checks all of the space marked free in the FAT for + bad clusters. If any clusters are bad they are marked bad in the + FAT. + +Arguments: + + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + USHORT clus, length, max_length; + ULONG start_sector, num_sectors, i; + NUMBER_SET bad_sectors; + LBN lbn; + ULONG percent_complete; + ULONG num_checked, total_to_check; + + Message->Set(MSG_CHK_RECOVERING_FREE_SPACE, PROGRESS_MESSAGE); + Message->Display(); + + percent_complete = 0; + Message->Set(MSG_PERCENT_COMPLETE); + Message->Display("%d", percent_complete); + + num_checked = 0; + total_to_check = _fat->QueryFreeClusters(); + max_length = QueryClusterCount()/20 + 1; + for (clus = FirstDiskCluster; _fat->IsInRange(clus); clus++) { + + for (length = 0; _fat->IsInRange(clus + length) && + _fat->IsClusterFree(clus + length) && + length < max_length; length++) { + } + + if (length) { + + start_sector = QueryStartDataLbn() + + (clus - FirstDiskCluster)*QuerySectorsPerCluster(); + num_sectors = length*QuerySectorsPerCluster(); + + if (!bad_sectors.Initialize() || + !_drive->Verify(start_sector, num_sectors, &bad_sectors)) { + + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + + for (i = 0; i < bad_sectors.QueryCardinality(); i++) { + lbn = bad_sectors.QueryNumber(i).GetLowPart(); + _fat->SetClusterBad((USHORT) ((lbn - QueryStartDataLbn())/ + QuerySectorsPerCluster()) + + FirstDiskCluster ); + } + + clus += length - 1; + num_checked += length; + + if (100*num_checked/total_to_check > percent_complete) { + percent_complete = 100*num_checked/total_to_check; + Message->Set(MSG_PERCENT_COMPLETE); + if (!Message->Display("%d", percent_complete)) { + return FALSE; + } + } + } + } + + percent_complete = 100; + Message->Set(MSG_PERCENT_COMPLETE); + if (!Message->Display("%d", percent_complete)) { + return FALSE; + } + + Message->Set(MSG_CHK_DONE_RECOVERING_FREE_SPACE, PROGRESS_MESSAGE); + Message->Display(); + + return TRUE; +} + +#endif // _SETUP_LOADER_ diff --git a/private/utils/ufat/src/fatsacnv.cxx b/private/utils/ufat/src/fatsacnv.cxx new file mode 100644 index 000000000..0825ece7a --- /dev/null +++ b/private/utils/ufat/src/fatsacnv.cxx @@ -0,0 +1,814 @@ +#include <pch.cxx> + +#define _NTAPI_ULIB_ +#define _UFAT_MEMBER_ + +#include "ulib.hxx" +#include "ufat.hxx" + +#include "cluster.hxx" +#include "cmem.hxx" +#include "error.hxx" +#include "fatdent.hxx" +#include "fatsa.hxx" +#include "rootdir.hxx" +#include "rtmsg.h" +#include "sortlist.hxx" +#include "sortlit.hxx" +#include "filedir.hxx" +#include "fat.hxx" +#include "reloclus.hxx" +#include "intstack.hxx" + +// #include "keyboard.hxx" + + + +UFAT_EXPORT +BOOLEAN +FAT_SA::QueryCensusAndRelocate ( + OUT PCENSUS_REPORT CensusReport, + IN OUT PINTSTACK RelocationStack, + OUT PBOOLEAN Relocated + ) + +/*++ + +Routine Description: + + This function serves a double purpose: + + 1.- Generates a census report containing the number and size of different + FAT structures. This report is somewhat similar to the ChkDsk report. + + 2.- Relocates clusters to free areas of the disk. + + Depending on the input parameters, this method may generate the census + report, relocate clusters, both, or none. + +Arguments: + + CensusReport - Supplies a pointer to the buffer that will contain + the census report. + + RelocationStack - Supplies a stack with the runs to be relocated. + The runs have to be pushed onto the stack as + (Start, Size) tuples, with the Size value being + pushed first. Both values must be given in sectors. + + + Relocated - Supplies pointer to boolean which is set to TRUE + if any cluster was relocated. + +Return Value: + + BOOLEAN - TRUE if (if requested) the census report was generated and + if (if requested) the clusters were relocated. +--*/ + +{ + USHORT RelocatedChain; // Chain of relocated clusters + SORTED_LIST ClustersToRelocate; // List of clusters to relocate + + *Relocated = FALSE; + + // + // Initialize the list of clusters to relocate and the relocated chain. + // The Relocated chain is the chain of clusters that have been relocated, + // it exists so that the clusters that have been freed by relocation + // are not re-used while relocating other clusters. + // + if ( !ClustersToRelocate.Initialize( ) ) { + return FALSE; + } + + RelocatedChain = 0; + + // + // If a Relocation stack is provided, relocate all clusters that can + // be easily relocated and put all other clusters in the + // ClustersToRelocate list. + // + // The clusters that are easily relocated are those that are not the + // first cluster of a chain. These clusters can be relocated by copying + // their contents and then patching the chain. Clusters that are the + // head of a chain require us to traverse the filesystem tree in order + // to locate their corresponding directory entry. + // + if ( RelocationStack ) { + + // + // InitRelocationList takes care of relocating all "easy" clusters. + // It puts the rest of the clusters (i.e. those that are head of + // chains) in the ClustersToRelocate list. + // + if ( !InitRelocationList( RelocationStack, + &RelocatedChain, + &ClustersToRelocate, + Relocated ) ) { + + ClustersToRelocate.DeleteAllMembers( ); + + return FALSE; + } + + // + // The RelocationStack should be empty. All the clusters that it + // specified have either been relocated or are now in the + // ClustersToRelocate list. + // + DebugAssert( RelocationStack->QuerySize() == 0 ); + + } + + // + // If we need to, traverse the directory tree obtaining the census + // and/or relocating the clusters in the ClustersToRelocate list. + // + if ( CensusReport || (ClustersToRelocate.QueryMemberCount() > 0 ) ) { + + if ( !DoVolumeCensusAndRelocation( CensusReport, + &ClustersToRelocate, + &RelocatedChain, + Relocated ) ) { + + ClustersToRelocate.DeleteAllMembers( ); + + return FALSE; + } + + DebugAssert( ClustersToRelocate.QueryMemberCount() == 0 ); + } + + // + // All the clusters in the relocation stack have been relocated, + // we can now free the clusters in the RelocatedChain. + // + if ( RelocatedChain ) { + _fat->FreeChain( RelocatedChain ); + } + + // + // Write the fat to disk + // + return Write(); +} + + + + +BOOLEAN +FAT_SA::InitRelocationList( + IN OUT PINTSTACK RelocationStack, + IN OUT PUSHORT RelocatedChain, + IN OUT PSORTED_LIST ClustersToRelocate, + OUT PBOOLEAN Relocated + ) +/*++ + +Routine Description: + + Takes runs of sectors out of a relocation stack and converts the sectors + into clusters. If a cluster is easily relocatable, this method relocates + the cluster and adds the cluster to the RelocatedChain. Otherwise the + cluster is added to the ClustersToRelocate list. + + If everything goes right, upon return the RelocationStack is empty, and + every cluster it specified is either in the RelocatedChain or in the + ClustersToRelocate list. + + Note that the relocation stack specifies runs of SECTORS. This method converts + the sectors to FAT clusters. + +Arguments: + + RelocationStack - Supplies the Relocation stack + RelocatedChain - Supplies the chain of relocated clusters + ClustersToRelocate - Supplies the list of cluisters to relocate + Relocated - Supplies pointer to relocated flag + +Return Value: + + BOOLEAN - TRUE if all the clusters in the runs were added to some + list. + +--*/ + +{ + + SORTED_LIST RelocatedList; // List of relocated clusters + PSORTED_LIST_ITERATOR ToRelocateIterator; // Iterates over ClustersToRelocate + PSORTED_LIST_ITERATOR RelocatedIterator; // Iterates over RelocatedList + BIG_INT FirstSector; // First sector in run + BIG_INT Size; // Size of run + BIG_INT Sector; // Iterates over each run + ULONG Offset; // Offset within run + USHORT Cluster; // Cluster to move + USHORT Previous; // Previous in chain + RELOCATION_CLUSTER TmpCluster; // Tmp. Cluster + PRELOCATION_CLUSTER RelCluster; // To put in cluster list + + + DebugPtrAssert( RelocationStack ); + DebugPtrAssert( RelocatedChain ); + DebugPtrAssert( ClustersToRelocate); + DebugAssert( ( RelocationStack->QuerySize() % 2 ) == 0 ); + + + if ( RelocatedList.Initialize() && + (RelocatedIterator = (PSORTED_LIST_ITERATOR)(RelocatedList.QueryIterator()) ) && + (ToRelocateIterator = (PSORTED_LIST_ITERATOR)(ClustersToRelocate->QueryIterator()) ) ) { + + while ( RelocationStack->QuerySize() > 0 ) { + + // + // Take a Run (i.e. a <FirstSector, Size> tuple) off the stack. + // + FirstSector = RelocationStack->Look( 0 ); + Size = RelocationStack->Look( 1 ); + RelocationStack->Pop( 2 ); + + DebugPrintf( " Relocating: Sector %X, size %X\n", FirstSector.GetLowPart(), Size.GetLowPart() ); + + // + // Convert the run into a sequence of clusters and determine + // what to do with those clusters. + // + Offset = 0; + while ( Offset < Size.GetLowPart() ) { + + // + // Get the sector to relocate + // + Sector = FirstSector + Offset; + + // + // Get the cluster in which that sector lives + // + Cluster = (USHORT)(((Sector - QueryStartDataLbn() )/ (ULONG)QuerySectorsPerCluster()).GetLowPart() + FirstDiskCluster); + + // + // Initialize the tmp. cluster and reset iterators + // + TmpCluster.Initialize( Cluster ); + RelocatedIterator->Reset(); + ToRelocateIterator->Reset(); + + DebugPrintf( " Cluster to relocate: %X ( Sector %X ) Contents %X \n", Cluster, Sector.GetLowPart(), _fat->QueryEntry( Cluster ) ); + + // + // If the cluster is already in a list, ignore it. + // + if ( !RelocatedIterator->FindNext( &TmpCluster ) && + !ToRelocateIterator->FindNext( &TmpCluster ) ) { + + // + // New cluster. Determine what to do with it + // + if ( !(RelCluster = NEW RELOCATION_CLUSTER) ) { + break; + } + + RelCluster->Initialize( Cluster ); + + if ( _fat->IsClusterFree( Cluster ) ) { + + // + // The cluster is already free. Add it to the + // RelocatedChain. + // + DebugPrintf( " Cluster %X already free\n", Cluster ); + *RelocatedChain = _fat->InsertChain( *RelocatedChain, Cluster ); + RelocatedList.Put( RelCluster ); + + } else if ( Previous = + // + // Assume that the clusters in the run are part of + // a chain. If the previous cluster number is the + // previous in the chain, then we don't have to + // search the FAT + // + (_fat->QueryEntry( Cluster - 1 ) == Cluster) ? + ( Cluster - 1 ) : + _fat->QueryPrevious( Cluster ) ) { + + // + // The cluster is not the first of a chain. We can + // relocate it right away. + // + DebugPrintf( " Cluster %X not head of chain\n", Cluster ); + if ( !RelocateOneCluster( Cluster, Previous ) ) { + DELETE( RelCluster ); + break; + } + *Relocated = TRUE; + *RelocatedChain = _fat->InsertChain( *RelocatedChain, Cluster ); + RelocatedList.Put( RelCluster ); + + } else if ( _fat->IsClusterBad( Cluster ) ) { + + // + // Bad cluster. We will stop relocating clusters. + // + DebugPrintf( " Cluster %X is Bad!\n", Cluster ); + DELETE( RelCluster ); + break; + + } else { + + // + // The first cluster of a chain. Some directory + // must make reference to it, but we don't know + // which directory, so we put the cluster in the + // ClusterToRelocate list. + // + DebugPrintf( " Cluster %X head of chain\n", Cluster ); + ClustersToRelocate->Put( RelCluster ); + } + } + + Offset++; + } + + // + // If could not process all the sectors in the run, something + // failed. Get out. + // + if ( Offset < Size.GetLowPart() ) { + break; + } + } + + DELETE( RelocatedIterator ); + DELETE( ToRelocateIterator ); + RelocatedList.DeleteAllMembers(); + + return ((RelocationStack->QuerySize() == 0) && ( Offset >= Size.GetLowPart() ) ); + } + + return FALSE; +} + + + +BOOLEAN +FAT_SA::RelocateFirstCluster( + IN OUT PFAT_DIRENT Dirent + ) + +/*++ + +Routine Description: + + Relocates the first cluster of the file described by a directory + entry. + +Arguments: + + Dirent - Supplies the directory entry + +Return Value: + + BOOLEAN - TRUE if cluster relocated. + +--*/ +{ + + HMEM Hmem; // Memory + USHORT OldCluster; // Original cluster + USHORT NewCluster; // New cluster + CLUSTER_CHAIN ClusterChain; // Cluster chain + + DebugPtrAssert( Dirent ); + + // + // Allocate a free cluster + // + if ( !Hmem.Initialize() || + !( NewCluster = _fat->AllocChain( 1 ) ) ) { + return FALSE; + } + + // + // Copy the contents of the cluster to the new cluster + // + OldCluster = Dirent->QueryStartingCluster(); + + // DebugPrintf( " Relocating cluster %X -> %X\n", OldCluster, NewCluster ); + + if ( ClusterChain.Initialize( &Hmem, _drive, this, _fat, OldCluster, 1 ) && + ClusterChain.Read() && + ClusterChain.Initialize( &Hmem, _drive, this, _fat, NewCluster, 1 ) && + ClusterChain.Write() ) { + + // + // Patch the cluster chain + // + _fat->SetEntry( NewCluster, _fat->QueryEntry( OldCluster ) ); + + // + // Patch the directory entry. Note that if we crash, we're ok because + // the new cluster that we point to is valid. The original cluster would + // be an orphan and can be recovered by ChkDsk. + // + Dirent->SetStartingCluster( NewCluster ); + _fat->SetClusterFree( OldCluster ); + + // DebugPrintf( " Directory entry patched.\n"); + return TRUE; + } + + // + // Could not relocate the cluster, Free the new cluster + // + _fat->FreeChain( NewCluster ); + return FALSE; +} + + + + + + +USHORT +FAT_SA::RelocateOneCluster( + IN USHORT Cluster, + IN USHORT Previous + ) + +/*++ + +Routine Description: + + Relocates one cluster given its cluster number and the previous + cluster in its chain. + +Arguments: + + Cluster - Supplies cluster to relocate + Previous - Supplies the previous cluster in the chain + +Return Value: + + USHORT - Cluster where the cluster was relocated + +--*/ +{ + + HMEM Hmem; // Memory + USHORT NewCluster; // New cluster + CLUSTER_CHAIN ClusterChain; // Cluster chain + + DebugAssert( Cluster ); + DebugAssert( Previous ); + + // + // Allocate a free cluster + // + if ( !Hmem.Initialize() || + !( NewCluster = _fat->AllocChain(1)) ) { + return FALSE; + } + + // DebugPrintf( " Relocating cluster %X -> %X\n", Cluster, NewCluster ); + + // + // Copy the contents of the cluster to the new cluster + // + if ( ClusterChain.Initialize( &Hmem, _drive, this, _fat, Cluster, 1 ) && + ClusterChain.Read() && + ClusterChain.Initialize( &Hmem, _drive, this, _fat, NewCluster, 1 ) && + ClusterChain.Write() ) { + + // + // Patch the chain. We set the new cluster first, so that if we crash, + // the previous cluster will always point to a valid cluster (either the + // original one or the new one), the chain will remain consistent and + // ChkDsk will remove orphans. + // + _fat->SetEntry( NewCluster, _fat->QueryEntry( Cluster ) ); + _fat->SetEntry( Previous, NewCluster ); + _fat->SetClusterFree( Cluster ); + + // DebugPrintf( " Done, Chain: Prev[%X (%X)] - [%X (%X)]\n", + // Previous, + // _fat->QueryEntry(Previous), + // NewCluster, + // _fat->QueryEntry(NewCluster) + // ); + + return NewCluster; + } + + // + // Could not relocate the cluster, Free the new cluster + // + _fat->FreeChain( NewCluster ); + return 0; +} + + + + + + +BOOLEAN +FAT_SA::DoVolumeCensusAndRelocation( + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ) +/*++ + +Routine Description: + + Does a volume census and/or relocates clusters in the supplied + relocation list. + +Arguments: + + CensusReport - Supplies pointer to the census report structure + ClustersToRelocate - Supplies pointer to list of clusters to relocate + RelocatedChain - Supplies pointer to chain of relocated clusters + Relocated - Supplies pointer to relocated flag + +Return Value: + + BOOLEAN - TRUE if census report obtained (if requested) and all + the clusters in the relocation list were relocated (if + requested). + +--*/ +{ + PFATDIR RootDir; // Root directory + FAT_DIRENT DirEnt; // Directory entry for iterating thru directory + DSTRING EAFile; // Name of EA file + + DebugAssert( !ClustersToRelocate || RelocatedChain ); + + // + // If there is something to do, do it. + // + if ( CensusReport || + (ClustersToRelocate && ClustersToRelocate->QueryMemberCount() > 0) ) { + + // + // Initialize the Census report if requested + // + if ( CensusReport ) { + CensusReport->FileEntriesCount = 0; + CensusReport->FileClusters = 0; + CensusReport->DirEntriesCount = 0; + CensusReport->DirClusters = 0; + CensusReport->EaClusters = 0; + } + + // + // Get root directory + // + RootDir = (PFATDIR)GetRootDir(); + DebugPtrAssert( RootDir ); + + // + // Do the census of the volume by recursively obtaining the + // census of the root directory. + // + if ( DoDirectoryCensusAndRelocation( RootDir, + CensusReport, + ClustersToRelocate, + RelocatedChain, + Relocated ) ) { + + // + // If a census report is requested, find out the size of + // the EA file (if it exists). + // + if ( CensusReport ) { + EAFile.Initialize( "EA DATA. SF" ); + + if ( DirEnt.Initialize( (PFAT_DIRENT)RootDir->SearchForDirEntry( &EAFile )) ) { + + CensusReport->EaClusters = + (USHORT)((USHORT)DirEnt.QueryFileSize()/ + (_drive->QuerySectorSize()*QuerySectorsPerCluster())+1); + } + } + } else { + + // + // Could not do the directory census + // + return FALSE; + } + } + + return TRUE; + +} + + + + +BOOLEAN +FAT_SA::DoDirectoryCensusAndRelocation( + IN OUT PFATDIR Directory, + IN OUT PCENSUS_REPORT CensusReport, + IN OUT PSORTED_LIST ClustersToRelocate, + IN OUT PUSHORT RelocatedChain, + OUT PBOOLEAN Relocated + ) +/*++ + +Routine Description: + + Does the census and cluster relocation of a directory an all + its subdirectories. + +Arguments: + + Directory - Supplies the directory + CensusReport - Supplies pointer to the census report structure + ClustersToRelocate - Supplies pointer to list of clusters to relocate + RelocatedChain - Supplies pointer to chain of relocated clusters + Relocated - Supplies pointer to relocated flag + +Return Value: + + BOOLEAN - TRUE if census report obtained (if requested) and all + the clusters in the relocation list (if provided) that + were references by this directory (or a subdirectory) + were relocated. + +--*/ + +{ + + FAT_DIRENT Dirent; // For iterating thru Dir + HMEM HMem; // Memory + FILEDIR FileDir; // Subdir + RELOCATION_CLUSTER TmpCluster; // Tmp. cluster + PRELOCATION_CLUSTER ClusterToRelocate; // Cluster to relocate + PRELOCATION_CLUSTER RelocatedCluster; // Cluster relocated + PITERATOR Iterator = NULL; // Iterates thru ClustersToRelocate + ULONG EntryNumber = 0; // Iterates thru Directorty + BOOLEAN Ok = TRUE; // FALSE if error + BOOLEAN RelocatedHere = FALSE; // True if relocated something + + DebugPtrAssert( Directory ); + + // + // If there are clusters to relocate, get an iterator for searching + // thru the list. + // + if ( ClustersToRelocate && ClustersToRelocate->QueryMemberCount() > 0 ) { + Iterator = ClustersToRelocate->QueryIterator(); + if ( !Iterator ) { + return FALSE; + } + } + + while ( Ok && + (CensusReport || (ClustersToRelocate && ClustersToRelocate->QueryMemberCount() > 0)) ) { + + // + // Get next directory entry and get out if we reach the end. + // + if ( !Dirent.Initialize( Directory->GetDirEntry( EntryNumber ) ) || + Dirent.IsEndOfDirectory() + ) { + break; + } + + // + // Ignore the deleted, the "parent" and the "self" entries + // and any long directory entries + // + if ( !( Dirent.IsErased() || + Dirent.IsDot() || + Dirent.IsDotDot() || + Dirent.IsLongEntry() ) ) { + + + // + // If a Relocation list is provided, and the first cluster + // of the entry is in the relocation list, then relocate + // the cluster. + // + if ( ClustersToRelocate && ClustersToRelocate->QueryMemberCount() > 0 ) { + + Iterator->Reset(); + TmpCluster.Initialize( Dirent.QueryStartingCluster() ); + + if ( ClusterToRelocate = (PRELOCATION_CLUSTER)Iterator->FindNext( &TmpCluster ) ) { + + // + // Cluster is in the relocation list, relocate it. + // + if ( !RelocateFirstCluster( &Dirent ) ) { + DebugAssert( FALSE ); + Ok = FALSE; + break; + } + + + // + // Cluster has been relocated. Remove the cluster from + // the relocation list and add it to the Relocated + // chain. + // + RelocatedCluster = (PRELOCATION_CLUSTER)ClustersToRelocate->Remove( Iterator ); + DebugAssert( RelocatedCluster == ClusterToRelocate ); + *RelocatedChain = _fat->InsertChain( *RelocatedChain, RelocatedCluster->QueryClusterNumber() ); + DELETE( RelocatedCluster ); + + // + // Set the Relocated flag so that we remember to write out + // the directory when we are done traversing it. + // + *Relocated = TRUE; + RelocatedHere = TRUE; + + } + } + + if ( Dirent.IsDirectory() ) { + + // + // This is a directory entry, update the census + // (if provided) and recurse. + // + if( !_fat->IsValidChain( Dirent.QueryStartingCluster() ) ) { + + Ok = FALSE; + break; + } + + if ( CensusReport ) { + CensusReport->DirEntriesCount++; + CensusReport->DirClusters += _fat->QueryLengthOfChain( Dirent.QueryStartingCluster() ); + } + + if ( !HMem.Initialize() || + + !FileDir.Initialize( &HMem, + _drive, + this, + _fat, + Dirent.QueryStartingCluster() ) || + + !FileDir.Read() || + + !DoDirectoryCensusAndRelocation( &FileDir, + CensusReport, + ClustersToRelocate, + RelocatedChain, + Relocated ) ) { + + // + // Something went wrong, we return failure + // + Ok = FALSE; + break; + } + + } else if ( !Dirent.IsVolumeLabel() ) { + + // + // This is a file entry, update the census + // (if provided ) + // + if( Dirent.QueryStartingCluster() != 0 && + !_fat->IsValidChain( Dirent.QueryStartingCluster() ) ) { + + Ok = FALSE; + break; + } + + if ( CensusReport ) { + CensusReport->FileEntriesCount++; + CensusReport->FileClusters += _fat->QueryLengthOfChain( Dirent.QueryStartingCluster() ); + } + } + } + + EntryNumber++; + } + + if ( Iterator ) { + DELETE( Iterator ); + } + + // + // If we relocated something, then we have patched some entry in this + // directory. Write it out to disk + // + if ( Ok && RelocatedHere ) { + Ok = Directory->Write(); + + DebugAssert( Ok ); + + } + + return Ok; +} diff --git a/private/utils/ufat/src/fatvol.cxx b/private/utils/ufat/src/fatvol.cxx new file mode 100644 index 000000000..eca57559c --- /dev/null +++ b/private/utils/ufat/src/fatvol.cxx @@ -0,0 +1,435 @@ +#include <pch.cxx> + +#include "error.hxx" +#include "message.hxx" +#include "rtmsg.h" +#include "wstring.hxx" + + +DEFINE_CONSTRUCTOR( FAT_VOL, VOL_LIODPDRV ); + +VOID +FAT_VOL::Construct ( + ) + +/*++ + +Routine Description: + + Constructor for FAT_VOL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // unreferenced parameters + (void)(this); +} + + +FAT_VOL::~FAT_VOL( + ) +/*++ + +Routine Description: + + Destructor for FAT_VOL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +BOOLEAN +FAT_VOL::Initialize( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN ExclusiveWrite, + IN BOOLEAN FormatMedia, + IN MEDIA_TYPE MediaType + ) +/*++ + +Routine Description: + + This routine initializes a FAT_VOL object. + +Arguments: + + NtDriveName - Supplies the drive path for the volume. + Message - Supplies an outlet for messages. + ExclusiveWrite - Supplies whether or not the drive should be + opened for exclusive write. + FormatMedia - Supplies whether or not to format the media. + MediaType - Supplies the type of media to format to. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + MESSAGE msg; + + Destroy(); + + if (!VOL_LIODPDRV::Initialize(NtDriveName, &_fatsa, Message, + ExclusiveWrite, FormatMedia, + MediaType)) { + Destroy(); + return FALSE; + } + + if (!Message) { + Message = &msg; + } + + if (FormatMedia) { + if (!_fatsa.Initialize(this, Message, FALSE)) { + Destroy(); + return FALSE; + } + } else { + if (!_fatsa.Initialize(this, &msg, TRUE) && + !_fatsa.Initialize(this, Message, FALSE)) { + Destroy(); + return FALSE; + } + + if (!_fatsa.Read(Message)) { + Destroy(); + return FALSE; + } + } + + return TRUE; +} + + +BOOLEAN +FAT_VOL::IsFileContiguous( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message, + OUT PULONG NumBlocks + ) +/*++ + +Routine Description: + + This routine computes the number of contiguous blocks for the given + file. If the file has only one block then the file is contiguous and + this function returns TRUE. Otherwise this function returns FALSE and + the number of blocks is optionally returned into 'NumBlocks'. + +Arguments: + + FullPathFileName - Supplies the file name of the file to check for + contiguity. + Message - Supplies an outlet for messages. + NumBlocks - Returns the number of contiguous blocks. + +Return Value: + + FALSE - The file is not contiguous. + TRUE - The file is contiguous. + +--*/ +{ + ULONG num_blocks; + PFAT fat; + USHORT clus; + DSTRING slash; + + if (NumBlocks) { + *NumBlocks = 0; + } + + if (!slash.Initialize("\\")) { + Message ? Message->Set(MSG_CHK_NO_MEMORY) : 1; + Message ? Message->Display("") : 1; + return FALSE; + } + + if (*FullPathFileName == slash) { + *NumBlocks = 1; + return TRUE; + } + + if ((clus = _fatsa.QueryFileStartingCluster(FullPathFileName)) == 1) { + Message ? Message->Set(MSG_FILE_NOT_FOUND) : 1; + Message ? Message->Display("%W", FullPathFileName) : 1; + return FALSE; + } + + if (clus == 0xFFFF) { + Message ? Message->Set(MSG_CHK_NO_MEMORY) : 1; + Message ? Message->Display("") : 1; + return FALSE; + } + + // Say that a zero length file is contiguous. + + if (clus == 0) { + *NumBlocks = 1; + return TRUE; + } + + + fat = _fatsa.GetFat(); + + DebugAssert(fat); + + for (num_blocks = 1; ; num_blocks++) { + while (!fat->IsEndOfChain(clus) && + (USHORT)(clus + 1) == fat->QueryEntry(clus)) { + clus++; + } + if (fat->IsEndOfChain(clus)) { + break; + } + clus = fat->QueryEntry(clus); + } + + if (NumBlocks) { + *NumBlocks = num_blocks; + } + + return num_blocks == 1; +} + + +BOOLEAN +FAT_VOL::ContiguityReport( + IN PCWSTRING DirectoryPath, + IN PCDSTRING FilesToCheck, + IN ULONG NumberOfFiles, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine generates a contiguity report for all of the 'FilesToCheck'. + These file are assumed to all be in the directory pointed to by + 'DirectoryPath'. + +Arguments: + + DirectoryPath - Supplies the directory containing the files to check. + FilesToCheck - Supplies an array of files to check. + NumberOfFiles - Supplies the number of files in the preceeding array. + Message - Suppliea an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + ULONG num_blocks; + PFAT fat; + USHORT clus; + DSTRING slash; + FILEDIR filedir; + PFATDIR dir; + HMEM hmem; + BOOLEAN all_contig; + FAT_DIRENT fatdent; + DSTRING current_file; + ULONG i; + DSTRING directory_path; + + if (!slash.Initialize("\\")) { + Message ? Message->Set(MSG_CHK_NO_MEMORY) : 1; + Message ? Message->Display() : 1; + return FALSE; + } + + if ((clus = _fatsa.QueryFileStartingCluster(DirectoryPath)) == 1) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", DirectoryPath); + return FALSE; + } + + if (clus == 0xFFFF) { + Message ? Message->Set(MSG_CHK_NO_MEMORY) : 1; + Message ? Message->Display() : 1; + return FALSE; + } + + fat = _fatsa.GetFat(); + + if (!clus) { + dir = _fatsa.GetRootDir(); + } else { + dir = &filedir; + if (!hmem.Initialize() || + !filedir.Initialize(&hmem, this, &_fatsa, fat, clus)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + + if (!filedir.Read()) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", DirectoryPath); + return FALSE; + } + } + + if (*DirectoryPath == slash) { + + if (!directory_path.Initialize("")) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + } else { + if (!directory_path.Initialize(DirectoryPath)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + } + + + all_contig = TRUE; + for (i = 0; i < NumberOfFiles; i++) { + + if (!current_file.Initialize(&directory_path) || + !current_file.Strcat(&slash) || + !current_file.Strcat(&FilesToCheck[i])) { + + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(); + return FALSE; + } + + if (fatdent.Initialize(dir->SearchForDirEntry(&FilesToCheck[i]))) { + + if (clus = fatdent.QueryStartingCluster()) { + + for (num_blocks = 1; ; num_blocks++) { + while (!fat->IsEndOfChain(clus) && + (USHORT)(clus + 1) == fat->QueryEntry(clus)) { + clus++; + } + if (fat->IsEndOfChain(clus)) { + break; + } + clus = fat->QueryEntry(clus); + } + + if (num_blocks != 1) { + Message->Set(MSG_CONTIGUITY_REPORT); + Message->Display("%W%d", ¤t_file, num_blocks); + all_contig = FALSE; + } + + } + + } else { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", ¤t_file); + all_contig = FALSE; + } + } + + if (all_contig) { + Message->Set(MSG_ALL_FILES_CONTIGUOUS); + Message->Display(""); + } + + return TRUE; +} + + +PVOL_LIODPDRV +FAT_VOL::QueryDupVolume( + IN PCWSTRING NtDriveName, + IN OUT PMESSAGE Message, + IN BOOLEAN ExclusiveWrite, + IN BOOLEAN FormatMedia, + IN MEDIA_TYPE MediaType + ) CONST +/*++ + +Routine Description: + + This routine allocates a FAT_VOL and initializes it to 'NtDriveName'. + +Arguments: + + NtDriveName - Supplies the drive path for the volume. + Message - Supplies an outlet for messages. + ExclusiveWrite - Supplies whether or not the drive should be + opened for exclusive write. + FormatMedia - Supplies whether or not to format the media. + MediaType - Supplies the type of media to format to. + +Return Value: + + A pointer to a newly allocated FAT volume. + +--*/ +{ + PFAT_VOL vol; + + // unreferenced parameters + (void)(this); + + if (!(vol = NEW FAT_VOL)) { + Message ? Message->Set(MSG_FMT_NO_MEMORY) : 1; + Message ? Message->Display("") : 1; + return NULL; + } + + if (!vol->Initialize(NtDriveName, Message, ExclusiveWrite, + FormatMedia, MediaType)) { + DELETE(vol); + return NULL; + } + + return vol; +} + + +VOID +FAT_VOL::Destroy( + ) +/*++ + +Routine Description: + + This routine returns a FAT_VOL object to its initial state. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // unreferenced parameters + (void)(this); +} diff --git a/private/utils/ufat/src/filedir.cxx b/private/utils/ufat/src/filedir.cxx new file mode 100644 index 000000000..cc307a26c --- /dev/null +++ b/private/utils/ufat/src/filedir.cxx @@ -0,0 +1,132 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" + +DEFINE_EXPORTED_CONSTRUCTOR( FILEDIR, FATDIR, UFAT_EXPORT ); + +VOID +FILEDIR::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FILEDIR. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _number_of_entries = 0; +} + + +UFAT_EXPORT +FILEDIR::~FILEDIR( + ) +/*++ + +Routine Description: + + Destructor for FILEDIR. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +FILEDIR::Initialize( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN PFAT_SA FatSuperArea, + IN PCFAT Fat, + IN USHORT StartingCluster + ) +/*++ + +Routine Description: + + This routine initializes the FILEDIR object for use. It will enable + referencing the directory starting at StartingCluster. + +Arguments: + + Mem - Supplies the memory for the cluster chain. + Drive - Supplies the drive on which the directory resides. + FatSuperArea - Supplies the super area for the FAT file system on + the drive. + Fat - Supplies the file allocation table for the drive. + StartingCluster - Supplies the first cluster of the directory. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + Destroy(); + + if (!_cluster.Initialize(Mem, Drive, FatSuperArea, Fat, StartingCluster)) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + _number_of_entries = Drive->QuerySectorSize()* + FatSuperArea->QuerySectorsPerCluster()* + _cluster.QueryLength()/ + BytesPerDirent; + + if (!_number_of_entries) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + return TRUE; +} + + +VOID +FILEDIR::Destroy( + ) +/*++ + +Routine Description: + + This routine returns the object to its initial state. Memory will + be freed and all other function besided Init will be inoperative. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _number_of_entries = 0; +} diff --git a/private/utils/ufat/src/makefile b/private/utils/ufat/src/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/utils/ufat/src/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/utils/ufat/src/makefile.inc b/private/utils/ufat/src/makefile.inc new file mode 100644 index 000000000..b1477410c --- /dev/null +++ b/private/utils/ufat/src/makefile.inc @@ -0,0 +1,14 @@ +{}.cxx{obj\i386\}.obj: + $(386_COMPILER) -Fo$@ $(MAKEDIR)\$(<F) + +{}.cxx{obj\mips\}.obj: + @-erase $@ >nul 2>&1 + @echo ClMips $< " $(C_COMPILER) " + @$(C_COMPILER) -Fo$@ $(MAKEDIR)\$< + +{}.cxx{obj\alpha\}.obj: + @-erase $@ >nul 2>&1 + @echo ClAlpha $< " $(ALPHA_COMPILER) " + @$(ALPHA_COMPILER) -Fo$@ $(MAKEDIR)\$< + +dummy: diff --git a/private/utils/ufat/src/mrcf.c b/private/utils/ufat/src/mrcf.c new file mode 100644 index 000000000..39816b175 --- /dev/null +++ b/private/utils/ufat/src/mrcf.c @@ -0,0 +1,1726 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Mrcf.c + +Abstract: + + This module implements the Compress routines for the Double Space File System + +Author: + + Gary Kimura [GaryKi] 26-May-1993 + +Revision History: + +--*/ + +#include <stdio.h> +#include <nt.h> +#include "mrcf.h" + +//XXX.mjb: +#define DbgAssert(x) /* nothing */ + +// +// The debug macros +// + +#ifdef MRCFDBG + +#define DbgDoit(X) {X;} +#define ChPrint(b) (isprint(b) ? b : '.') +#define DbgPrint printf + +#else + +#define DbgDoit(X) {NOTHING;} + +#endif // MRCFDBG + + +// +// Compress this much before each EOS +// + +#define cbCOMPRESSCHUNK (512) + +// +// Maximum back-pointer value, also used to indicate end of compressed stream! +// + +#define wBACKPOINTERMAX (4415) + +// +// bitsEND_OF_STREAM - bits that mark end of compressed stream (EOS) +// +// This pattern is used to indicate the end of a "chunk" in a compressed +// data stream. The Compress code compresses up to 512 bytes, writes +// this pattern, and continues. +// +// NOTE: This diagram is interpreted right to left. +// +// ? ---offset---- +// +// ?.111-1111-1111-1.1.11 +// +// \---7F---/ \----FF---/ +// +// This is a 12-bit "match" code with a maximum offset. +// NOTE: There is no length component! +// +// Define the EOS and also say how many bits it is. +// + +#define bitsEND_OF_STREAM (0x7FFF) +#define cbitsEND_OF_STREAM (15) + +// +// MDSIGNATURE - Signature at start of each compressed block +// +// This 4-byte signature is used as a check to ensure that we +// are decompressing data we compressed, and also to indicate +// which compression method was used. +// +// NOTE: A compressed block consists of one or more "chunks", separated +// by the bitsEND_OF_STREAM pattern. +// +// Byte Word +// ----------- --------- +// 0 1 2 3 0 1 Meaning +// -- -- -- -- ---- ---- ---------------- +// 44 53 00 01 5344 0100 MaxCompression +// 44 53 00 02 5344 0200 StandardCompression +// +// NOTE: The *WORD* values are listed to be clear about the +// byte ordering! +// + +typedef struct _MDSIGNATURE { + + // + // Must be MD_STAMP + // + + USHORT sigStamp; + + // + // mdsSTANDARD or mdsMAX + // + + USHORT sigType; + +} MDSIGNATURE; +typedef MDSIGNATURE *PMDSIGNATURE; + +#define MD_STAMP 0x5344 // Signature stamp at start of compressed blk +#define mdsSTANDARD 0x0200 // StandardCompressed block +#define MASK_VALID_mds 0x0300 // All other bits must be zero + + +// +// Local procedure declarations and macros +// + +#define min(a,b) (a < b ? a : b) + +// +// PFNFINDMATCH - Lookup function type for XxxxCompression routines +// + +typedef ULONG (*PFNFINDMATCH) ( + ULONG UncompressedIndex, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PULONG MatchedStringIndex, + PMRCF_STANDARD_COMPRESS WorkSpace + ); + +// +// Local procedure prototypes +// + +ULONG +MrcfDoCompress ( + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PFNFINDMATCH FindMatchFunction, + PMRCF_STANDARD_COMPRESS WorkSpace + ); + +ULONG +MrcfCompressChunk ( + PUCHAR UncompressedBuffer, + ULONG UncompressedIndex, + ULONG UncompressedLength, + PFNFINDMATCH FindMatchFunction, + PMRCF_STANDARD_COMPRESS WorkSpace + ); + +ULONG +MrcfFindMatchStandard ( + ULONG UncompressedIndex, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PULONG MatchedStringIndex, + PMRCF_STANDARD_COMPRESS WorkSpace + ); + +ULONG +MrcfGetMatchLength ( + PUCHAR UncompressedBuffer, + ULONG MatchIndex, + ULONG CurrentIndex, + ULONG UncompressedLength + ); + +BOOLEAN +MrcfEncodeByte ( + UCHAR b, + PMRCF_BIT_IO BitIo + ); + +BOOLEAN +MrcfEncodeMatch ( + ULONG off, + ULONG cb, + PMRCF_BIT_IO BitIo + ); + +VOID +MrcfSetBitBuffer ( + PUCHAR pb, + ULONG cb, + PMRCF_BIT_IO BitIo + ); + +VOID +MrcfFillBitBuffer ( + PMRCF_BIT_IO BitIo + ); + +USHORT +MrcfReadBit ( + PMRCF_BIT_IO BitIo + ); + +USHORT +MrcfReadNBits ( + LONG cbits, + PMRCF_BIT_IO BitIo + ); + +BOOLEAN +MrcfWriteBit ( + ULONG bit, + PMRCF_BIT_IO BitIo + ); + +BOOLEAN +MrcfWriteNBits ( + ULONG abits, + LONG cbits, + PMRCF_BIT_IO BitIo + ); + +ULONG +MrcfFlushBitBuffer ( + PMRCF_BIT_IO BitIo + ); + +//**** unconverted routines **** + +VOID +MrcfDoInterMaxPairs ( + ULONG ibU, + PUCHAR pbU, + ULONG cbMatch, + PVOID WorkSpace + ); + +ULONG +MrcfDoMaxPairLookup ( + ULONG ibU, + PUCHAR pbU, + PVOID WorkSpace + ); + +ULONG +MrcfFindMatchMax ( + ULONG ibU, + PUCHAR pbU, + ULONG cbU, + PULONG piPrev, + BOOLEAN fLast, + PVOID WorkSpace + ); + + +ULONG +MrcfDecompress ( + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PMRCF_DECOMPRESS WorkSpace + ) + +/*++ + +Routine Description: + + This routine decompresses a buffer of StandardCompressed or MaxCompressed + data. + +Arguments: + + UncompressedBuffer - buffer to receive uncompressed data + + UncompressedLength - length of UncompressedBuffer + + NOTE: UncompressedLength must be the EXACT length of the uncompressed + data, as Decompress uses this information to detect + when decompression is complete. If this value is + incorrect, Decompress may crash! + + CompressedBuffer - buffer containing compressed data + + CompressedLength - length of CompressedBuffer + + WorkSpace - pointer to a private work area for use by this operation + +Return Value: + + ULONG - Returns the size of the decompressed data in bytes. Returns 0 if + there was an error in the decompress. + +--*/ + +{ + ULONG cbMatch; // Length of match string + ULONG i; // Index in UncompressedBuffer to receive decoded data + ULONG iMatch; // Index in UncompressedBuffer of matched string + ULONG k; // Number of bits in length string + ULONG off; // Offset from i in UncompressedBuffer of match string + USHORT x; // Current bit being examined + ULONG y; + + // + // verify that compressed data starts with proper signature + // + + if (CompressedLength < sizeof(MDSIGNATURE) || // Must have signature + ((PMDSIGNATURE)CompressedBuffer)->sigStamp != MD_STAMP || // Stamp must be OK + ((PMDSIGNATURE)CompressedBuffer)->sigType & (~MASK_VALID_mds)) { // Type must be OK + + return 0; + } + + // + // Skip over the valid signature + // + + CompressedLength -= sizeof(MDSIGNATURE); + CompressedBuffer += sizeof(MDSIGNATURE); + + // + // Set up for decompress, start filling UncompressedBuffer at front + // + + i = 0; + + // + // Set statics to save parm passing + // + + MrcfSetBitBuffer(CompressedBuffer,CompressedLength,&WorkSpace->BitIo); + + while (TRUE) { + + DbgDoit( DbgPrint("UncompressedOffset i = %3x ",i) ); + DbgDoit( DbgPrint("CompressedOffset = (%3x.%2x) ", WorkSpace->BitIo.cbBBInitial - WorkSpace->BitIo.cbBB, 16 - WorkSpace->BitIo.cbitsBB) ); + + y = MrcfReadNBits(2,&WorkSpace->BitIo); + + // + // Check if next 7 bits are a byte + // 1 if 128..255 (0x80..0xff), 2 if 0..127 (0x00..0x7f) + // + + if (y == 1 || y == 2) { + + DbgAssert(i<UncompressedLength); + + UncompressedBuffer[i] = (UCHAR)((y == 1 ? 0x80 : 0) | MrcfReadNBits(7,&WorkSpace->BitIo)); + + DbgDoit( DbgPrint("byte: %02x = '%c'\n",(USHORT)UncompressedBuffer[i],ChPrint(UncompressedBuffer[i])) ); + + i++; + + } else { + + // + // Have match sequence + // + + DbgDoit( DbgPrint("offset(") ); + + // + // Get the offset + // + + if (y == 0) { + + // + // next 6 bits are offset + // + + off = MrcfReadNBits(6,&WorkSpace->BitIo); + + DbgDoit( DbgPrint("%x): %x",6,off) ); + + DbgAssert(off != 0); + + } else { + + x = MrcfReadBit(&WorkSpace->BitIo); + + if (x == 0) { + + // + // next 8 bits are offset-64 (0x40) + // + + off = MrcfReadNBits(8, &WorkSpace->BitIo) + 64; + + DbgDoit( DbgPrint("%x): %x",8,off) ); + + } else { + + // + // next 12 bits are offset-320 (0x140) + // + + off = MrcfReadNBits(12, &WorkSpace->BitIo) + 320; + + DbgDoit( DbgPrint("%x): %x",12,off) ); + + if (off == wBACKPOINTERMAX) { + + // + // EOS marker + // + + DbgDoit( DbgPrint("; EOS\n") ); + + if (i >= UncompressedLength) { + + // + // Done with entire buffer + // + + DbgDoit( DbgPrint("Uncompressed Length = %x\n",i) ); + + return i; + + } else { + + // + // More to do + // Done with a 512-byte chunk + // + + continue; + } + } + } + } + + DbgAssert(i<UncompressedLength); + DbgAssert(off <= i); + + // + // Get the length - logarithmically encoded + // + + for (k=0; (x=MrcfReadBit(&WorkSpace->BitIo)) == 0; k++) { NOTHING; } + + DbgAssert(k <= 8); + + if (k == 0) { + + // + // All matches at least 2 chars long + // + + cbMatch = 2; + + } else { + + cbMatch = (1 << k) + 1 + MrcfReadNBits(k, &WorkSpace->BitIo); + } + + DbgDoit( DbgPrint("; length=%x; '",cbMatch) ); + + DbgAssert((i - off + cbMatch - 1) <= UncompressedLength); + + // + // Copy the matched string + // + + iMatch = i - off; + + while ( (cbMatch > 0) && (i<UncompressedLength) ) { + + DbgDoit( DbgPrint("%c",ChPrint(UncompressedBuffer[iMatch])) ); + + UncompressedBuffer[i++] = UncompressedBuffer[iMatch++]; + cbMatch--; + } + + DbgDoit( DbgPrint("'\n") ); + + DbgAssert(cbMatch == 0); + } + } +} + + +ULONG +MrcfStandardCompress ( + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PMRCF_STANDARD_COMPRESS WorkSpace + ) + +/*++ + +Routine Description: + + This routine compresses a buffer using the standard compression algorithm. + +Arguments: + + CompressedBuffer - buffer to receive compressed data + + CompressedLength - length of CompressedBuffer + + UncompressedBuffer - buffer containing uncompressed data + + UncompressedLength - length of UncompressedBuffer + + WorkSpace - pointer to a private work area for use by this operation + +Return Value: + + ULONG - Returns the size of the compressed data in bytes. Returns 0 if + the data is not compressible + +--*/ + +{ + ULONG i,j; + + // + // Fill lookup tables with initial values + // + + for (i=0; i<256; i++) { + + for (j = 0; j < cMAXSLOTS; j++) { + + WorkSpace->ltX[i][j] = ltUNUSED; // Mark offset look-up entries unused + WorkSpace->abChar[i][j] = bRARE; // Mark match char entries unused + } + + WorkSpace->abMRUX[i] = mruUNUSED; // MRU pointer = unused + } + + // + // Do compression, first set type and then do the compression + // + + ((PMDSIGNATURE)CompressedBuffer)->sigType = mdsSTANDARD; + + return MrcfDoCompress( CompressedBuffer, + CompressedLength, + UncompressedBuffer, + UncompressedLength, + MrcfFindMatchStandard, + WorkSpace ); +} + + +// +// Internal Support Routine +// + +ULONG +MrcfDoCompress ( + PUCHAR CompressedBuffer, + ULONG CompressedLength, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PFNFINDMATCH FindMatchFunction, + PMRCF_STANDARD_COMPRESS WorkSpace + ) + +/*++ + +Routine Description: + + This routine compresses a data buffer + +Arguments: + + CompressedBuffer - buffer to receive compressed data + + CompressedLength - length of CompressedBuffer + + UncompressedBuffer - buffer containing uncompressed data + + UncompressedLength - length of UncompressedBuffer + + FindMatchFunction - matching function + + WorkSpace - Supplies a pointer to the bit buffer statics + +Return Value: + + ULONG - Returns the size of the compressed data in bytes. Returns 0 if + the data is not compressible + +--*/ + +{ + ULONG cbDone; // Count of uncompressed bytes processed so far + ULONG cb; // Count of bytes processed in a chunk + + DbgAssert(CompressedLength >= UncompressedLength); + + // + // Treat zero-length request specially as Not compressible + // + + if (UncompressedLength == 0) { return 0; } + + // + // Write signature to compressed data block + // + + ((PMDSIGNATURE)CompressedBuffer)->sigStamp = MD_STAMP; + + CompressedLength -= sizeof(MDSIGNATURE); + CompressedBuffer += sizeof(MDSIGNATURE); + + // + // Set statics to save parm passing + // + + MrcfSetBitBuffer(CompressedBuffer, CompressedLength, &WorkSpace->BitIo); + + // + // Start with first chunk + // and process entire buffer + // + + cbDone = 0; + + while (cbDone < UncompressedLength) { + + // + // Compress a chunk + // + + cb = MrcfCompressChunk( UncompressedBuffer, + cbDone, + UncompressedLength, + FindMatchFunction, + WorkSpace ); + + // + // Check if we could not compress, i.e., Not compressible + // + + if (cb == 0) { return 0; } + + cbDone += cb; + + if (FALSE) { //**** if (WorkSpace->fMaxCmp) { + + // + // MAXCMP check + // + + if ((cbDone < UncompressedLength) && (WorkSpace->BitIo.cbBB < 586)) { return 0; } + + } else { + + // + // RCOMP check + // + + //**** if (WorkSpace->BitIo.cbBB <= 586) { return 0; } + } + } + + DbgAssert(cbDone == UncompressedLength); + + // + // Make sure we saved some space + // + + cb = sizeof(MDSIGNATURE) + MrcfFlushBitBuffer( &WorkSpace->BitIo ); + + if (TRUE) { //**** if (!WorkSpace->fMaxCmp) { + + if (cb+8 >= UncompressedLength) { return 0; } + } + + if (cb < UncompressedLength) { + + return cb; + + } else { + + return 0; + } +} + + +// +// Internal Support Routine +// + +ULONG +MrcfCompressChunk ( + PUCHAR UncompressedBuffer, + ULONG UncompressedIndex, + ULONG UncompressedLength, + PFNFINDMATCH FindMatchFunction, + PMRCF_STANDARD_COMPRESS WorkSpace + ) + +/*++ + +Routine Description: + + This routine compresses a chunk of uncompressed data + +Arguments: + + UncompressedBuffer - buffer containing uncompressed data + + UncompressedIndex - index in UncompressedBuffer to start compressing (0 => first byte) + + UncompressedLength - length of UncompressedBuffer + + FindMatchFunction - matching function + + WorkSpace - Supplies a pointer to the bit buffer statics + +Return Value: + + ULONG - Returns the non-zero count of uncompressed bytes processed. + Returns 0 if the data is not compressible + +--*/ + +{ + UCHAR b1; // First byte of pair + UCHAR b2; // Second byte of pair + ULONG cbChunk; // Count of bytes in chunk to compress + ULONG cbMatch; // Count of bytes matched + ULONG cbUChunk; // Phony buffer length, for compressing this chunk + BOOLEAN fLast; // TRUE if this is the last chunk + ULONG i; // Index in byte stream being compressed + ULONG iPrev; // Previous table entry + + DbgAssert(UncompressedLength > 0); + DbgAssert(UncompressedBuffer != 0); + DbgAssert(UncompressedIndex < UncompressedLength); + + // + // Only compress one chunk + // + + cbChunk = min((UncompressedLength-UncompressedIndex),cbCOMPRESSCHUNK); + + // + // Limit to chunk length + // + + cbUChunk = UncompressedIndex + cbChunk; + + DbgAssert(cbUChunk <= UncompressedLength); + + // + // TRUE if last chunk of buffer + // + + fLast = (cbUChunk == UncompressedLength); + + // + // Limit to chunk length + // + + UncompressedLength = cbUChunk; + + // + // Scan each pair of bytes + // + + // + // First byte of input + // + + b2 = UncompressedBuffer[UncompressedIndex]; + + // + // Process all bytes in chunk + // + + for (i=UncompressedIndex+1; i<UncompressedLength; ) { + + // + // Set Last byte, Next byte, and find a match + // + + b1 = b2; + b2 = UncompressedBuffer[i]; + + cbMatch = (*FindMatchFunction)(i,UncompressedBuffer,UncompressedLength,&iPrev,WorkSpace); + + // + // Check if we got match + // + + if (cbMatch >= 2) { + + DbgDoit( DbgPrint("<Match>: '%c%c' at offset %x for length %x\n", + ChPrint(UncompressedBuffer[i-1]),ChPrint(UncompressedBuffer[i]),i-1, cbMatch) ); + + // + // Pass offset and length, and check for failure (i.e., data incompressible) + // + + if (!MrcfEncodeMatch(i-iPrev,cbMatch,&WorkSpace->BitIo)) { + + return 0; + } + + // + // Now we have to continue with the first pair of bytes + // after the string we matched: mmmmmmm12 + // + + // + // i is index of 2nd byte after match! + // + + i += cbMatch; + + // + // Check if at least 1 byte still to compress, if so + // get 1st byte after match, for loop, otherwise put out EOS + // + + if (i <= UncompressedLength) { + + b2 = UncompressedBuffer[i-1]; + + } else { + + goto WriteEOS; + } + + } else { + + // + // No match found, Store one byte and continue, and check + // for failure (i.e., data incompressible) + // + + if (!MrcfEncodeByte(b1,&WorkSpace->BitIo)) { + return 0; + } + + // + // Advance to next byte + // + + i++; + } + } + + // + // Store last byte, and again check for failure + // + + if (!MrcfEncodeByte(b2,&WorkSpace->BitIo)) { + + return 0; + } + +WriteEOS: + + // + // write out EOS, and check for failure otherwise return how much + // data we processed + // + + if (!MrcfWriteNBits( bitsEND_OF_STREAM, + cbitsEND_OF_STREAM, + &WorkSpace->BitIo )) { + + return 0; + + } else { + + return cbChunk; + } +} + + +// +// Internal Support Routine +// + +ULONG +MrcfFindMatchStandard ( + ULONG UncompressedIndex, + PUCHAR UncompressedBuffer, + ULONG UncompressedLength, + PULONG MatchedStringIndex, + PMRCF_STANDARD_COMPRESS WorkSpace + ) + +/*++ + +Routine Description: + + This routine does a standard compression lookup + +Arguments: + + UncompressedIndex - index into UncompressedBuffer[] of *2nd* byte of pair to match + + UncompressedBuffer - buffer containing uncompressed data + + UncompressedLength - length of UncompressedBuffer + + MatchedStringIndex - pointer to int to receive index of start of matched string + + WorkSpace - Supplies a pointer to the bit buffer statics + +Return Value: + + ULONG - Returns length of match. If the return value is >= 2 then + *MatchedStringIndex = index of matched string (*2nd* byte in pair). + Otherwise the match length is 0 or 1 + +--*/ + +{ + ULONG i; + ULONG iMRU; + ULONG iChar; + ULONG iPrev; + + // + // Are there exactly two bytes left? If so then do not check for match. + // + + if (UncompressedIndex == (UncompressedLength-1)) { return 0; } + + // + // 1st char is index to look-up tables + // + + iChar = UncompressedBuffer[UncompressedIndex-1]; + + // + // Can't match if 1st MRU ent is unused + // + + if (WorkSpace->abMRUX[iChar] != mruUNUSED) { + + for (i = 0; i < cMAXSLOTS; i++) { + + if (WorkSpace->abChar[iChar][i] == UncompressedBuffer[UncompressedIndex]) { + + iPrev = WorkSpace->ltX[iChar][i]; + WorkSpace->ltX[iChar][i] = UncompressedIndex; + + if ((UncompressedIndex - iPrev) >= wBACKPOINTERMAX) { return 0; } + + *MatchedStringIndex = iPrev; + + return MrcfGetMatchLength( UncompressedBuffer, + iPrev, + UncompressedIndex, + UncompressedLength ); + } + } + } + + // + // Cycle MRU index for char + // Update char match table + // Location of this char pair + // + + iMRU = (WorkSpace->abMRUX[iChar] += 1) & (cMAXSLOTS - 1); + WorkSpace->abChar[iChar][iMRU] = UncompressedBuffer[UncompressedIndex]; + WorkSpace->ltX[iChar][iMRU] = UncompressedIndex; + + return 0; +} + + +// +// Internal Support Routine +// + +ULONG +MrcfGetMatchLength ( + PUCHAR UncompressedBuffer, + ULONG MatchIndex, + ULONG CurrentIndex, + ULONG UncompressedLength + ) + +/*++ + +Routine Description: + + Find length of matching strings + +Arguments: + + UncompressedBuffer - uncompressed data buffer + + MatchIndex - index of 2nd byte in UncompressedBuffer of match (MatchIndex < CurrentIndex) + + CurrentIndex - index of 2nd byte in UncompressedBuffer that is being compressed + + UncompressedLength - length of UncompressedBuffer + +Return Value: + + ULONG - Returns length of matching strings (0, or 2 or greater) + +--*/ + +{ + ULONG cb; + + DbgAssert(MatchIndex >= 0); + DbgAssert(MatchIndex < CurrentIndex); + DbgAssert(CurrentIndex < UncompressedLength); + + // + // Point back to start of both strings + // + + MatchIndex--; + CurrentIndex--; + + // + // No bytes matched, yet + // + + cb = 0; + + // + // Scan for end of match, or end of buffer + // + + while ((CurrentIndex<UncompressedLength) && (UncompressedBuffer[MatchIndex] == UncompressedBuffer[CurrentIndex])) { + + MatchIndex++; + CurrentIndex++; + cb++; + } + + return cb; +} + + +// +// Internal Support Routine +// + +BOOLEAN +MrcfEncodeByte ( + UCHAR b, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Write one byte to compressed bit stream + +Arguments: + + b - byte to write + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + BOOLEAN - TRUE if the bit stream was updated and FALSE if overran buffer + +--*/ + +{ + ULONG abits; + + DbgDoit( DbgPrint("<MrcfEncodeByte>: byte=%02x '%c'\n",b,ChPrint(b)) ); + + abits = ((b & 0x7F) << 2) | ((b < 128) ? 2 : 1); + + // + // Write to bitstream + // + + return MrcfWriteNBits(abits, 9, BitIo); +} + + +// +// Internal Support Routine +// + +BOOLEAN +MrcfEncodeMatch ( + ULONG off, + ULONG cb, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Write a match to compressed bit stream + +Arguments: + + off - offset of match (must be greater than 0) + + cb - length of match (must be at least 2) + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + BOOLEAN - TRUE if the compress stream was updated and FALSE if overran buffer + +--*/ + +{ + ULONG abits; + ULONG cbits; + ULONG cbSave; + ULONG mask; + + DbgAssert(off > 0); + DbgAssert(off < wBACKPOINTERMAX); + DbgAssert(cb >= 2); + + DbgDoit( DbgPrint("<MrcfEncodeMatch>: off=%x len=%x\n",off,cb) ); + + // + // Encode the match bits and offset portion + // + + if (off < 64) { + + // + // Use 6-bit offset encoding + // + + abits = (off << 2) | 0x0; // .00 = <offset>+<6-bit>+<match> + + if (!MrcfWriteNBits(abits,6+2,BitIo)) { + + // + // Opps overran the compression buffer + // + + return FALSE; + } + + } else if (off < 320) { + + // + // Use 8-bit offset encoding + // + + abits = ((off - 64) << 3) | 0x3; // 0.11 = <offset>+<8-bit>+<match> + + if (!MrcfWriteNBits(abits,8+3,BitIo)) { + + // + // Opps overran the compression buffer + // + + return FALSE; + } + + } else { // (off >= 320) + + // + // Use 12-bit offset encoding + // + + abits = ((off - 320) << 3) | 0x7; // 1.11 = <offset>+<12-bit>+<match> + + if (!MrcfWriteNBits(abits,12+3,BitIo)) { + + // + // Opps overran the compression buffer + // + + return FALSE; + } + } + + // + // Encode the length logarithmically + // + + cb -= 1; + cbSave = cb; // Save to get remainder later + cbits = 0; + + while (cb > 1) { + + cbits++; + + // + // Put out another 0 for the length, and + // watch for buffer overflow + // + + if (!MrcfWriteBit(0, BitIo)) { + + return FALSE; + } + + // + // Shift count right (avoid sign bit) + // + + ((USHORT)cb) >>= 1; + } + + // + // Terminate length bit string + // + + if (!MrcfWriteBit(1, BitIo)) { + + return FALSE; + } + + if (cbits > 0) { + + // + // Mask for bits we want, and get remainder + // + + mask = (1 << cbits) - 1; + abits = cbSave & mask; + + if (!MrcfWriteNBits(abits,cbits,BitIo)) { + + return FALSE; + } + } + + return TRUE; +} + + +// +// Internal Support Routine +// + +VOID +MrcfSetBitBuffer ( + PUCHAR pb, + ULONG cb, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Set statics with coded buffer pointer and length + +Arguments: + + pb - pointer to compressed data buffer + + cb - length of compressed data buffer + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + None. + +--*/ + +{ + BitIo->pbBB = pb; + BitIo->cbBB = cb; + BitIo->cbBBInitial = cb; + BitIo->cbitsBB = 0; + BitIo->abitsBB = 0; +} + + +// +// Internal Support Routine +// + +VOID +MrcfFillBitBuffer ( + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Fill abitsBB from static bit buffer + +Arguments: + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + None. + +--*/ + +{ + DbgAssert((BitIo->cbitsBB) == 0); + + switch (BitIo->cbBB) { + + case 0: + + DbgAssert(FALSE); + + break; + + case 1: + + // + // Get last byte and adjust count + // + + BitIo->cbitsBB = 8; + BitIo->abitsBB = *(BitIo->pbBB)++; + BitIo->cbBB--; + + break; + + default: + + // + // Get word and adjust count + // + + BitIo->cbitsBB = 16; + BitIo->abitsBB = *((USHORT *)(BitIo->pbBB))++; + BitIo->cbBB -= 2; + + break; + } +} + + +// +// Internal Support Routine +// + +USHORT +MrcfReadBit ( + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Get next bit from bit buffer + +Arguments: + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + USHORT - Returns next bit (0 or 1) + +--*/ + +{ + USHORT bit; + + // + // Check if no bits available + // + + if ((BitIo->cbitsBB) == 0) { + + MrcfFillBitBuffer(BitIo); + } + + // + // Decrement the bit count + // get the bit, remove it, and return the bit + // + + (BitIo->cbitsBB)--; + bit = (BitIo->abitsBB) & 1; + (BitIo->abitsBB) >>= 1; + + return bit; +} + + +// +// Internal Support Routine +// + +USHORT +MrcfReadNBits ( + LONG cbits, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Get next N bits from bit buffer + +Arguments: + + cbits - count of bits to get + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + USHORT - Returns next cbits bits. + +--*/ + +{ + ULONG abits; // Bits to return + LONG cbitsPart; // Partial count of bits + ULONG cshift; // Shift count + ULONG mask; // Mask + + // + // Largest number of bits we should read at one time is 12 bits for + // a 12-bit offset. The largest length field component that we + // read is 8 bits. If this routine were used for some other purpose, + // it can support up to 15 (NOT 16) bit reads, due to how the masking + // code works. + // + + DbgAssert(cbits <= 12); + + // + // No shift and no bits yet + // + + cshift = 0; + abits = 0; + + while (cbits > 0) { + + // + // If not bits available get some bits + // + + if ((BitIo->cbitsBB) == 0) { + + MrcfFillBitBuffer(BitIo); + } + + // + // Number of bits we can read + // + + cbitsPart = min((BitIo->cbitsBB), cbits); + + // + // Mask for bits we want, extract and store them + // + + mask = (1 << cbitsPart) - 1; + abits |= ((BitIo->abitsBB) & mask) << cshift; + + // + // Remember the next chunk of bits + // + + cshift = cbitsPart; + + // + // Update bit buffer, move remaining bits down and + // update count of bits left + // + + (BitIo->abitsBB) >>= cbitsPart; + (BitIo->cbitsBB) -= cbitsPart; + + // + // Update count of bits left to read + // + + cbits -= cbitsPart; + } + + // + // Return requested bits + // + + return (USHORT)abits; +} + + +// +// Internal Support Routine +// + +BOOLEAN +MrcfWriteBit ( + ULONG bit, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Write a bit to the bit buffer + +Arguments: + + bit - bit to write (0 or 1) + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + BOOLEAN - returns TRUE if the compresed bit stream was updated and + FALSE if overran buffer. + +--*/ + +{ + DbgAssert((bit == 0) || (bit == 1)); + DbgAssert((BitIo->cbitsBB) < 16); + + DbgDoit( DbgPrint("<MrcfWriteBit>: bit=%x\n",bit) ); + + // + // Write one bit + // + + (BitIo->abitsBB) |= bit << (BitIo->cbitsBB); + (BitIo->cbitsBB)++; + + // + // Check if abitsBB is full and write compressed data buffer + // + + if ((BitIo->cbitsBB) >= 16) { + + return (MrcfFlushBitBuffer(BitIo) != 0); + + } else { + + return TRUE; + } +} + + +// +// Internal Support Routine +// + +BOOLEAN +MrcfWriteNBits ( + ULONG abits, + LONG cbits, + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Write N bits to the bit buffer + +Arguments: + + abits - bits to write + + cbits - count of bits write + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + BOOLEAN - returns TRUE if the compressed bit stream was updated and + FALSE if overran buffer + +--*/ + +{ + LONG cbitsPart; + ULONG mask; + + DbgAssert(cbits > 0); + DbgAssert(cbits <= 16); + DbgAssert((BitIo->cbitsBB) < 16); + + DbgDoit( DbgPrint("<MrcfWriteNBits>: bits=%04x count=%x\n",abits,cbits) ); + + while (cbits > 0) { + + // + // Number of bits we can write + // + + cbitsPart = min(16-(BitIo->cbitsBB), cbits); + + mask = (1 << cbitsPart) - 1; + + // + // Move part of bits to buffer + // + + (BitIo->abitsBB) |= (abits & mask) << (BitIo->cbitsBB); + + // + // Update count of bits written + // + + (BitIo->cbitsBB) += cbitsPart; + + // + // Check if buffer if full + // + + if ((BitIo->cbitsBB) >= 16) { + + // + // Write compressed data buffer + // + + if (!MrcfFlushBitBuffer(BitIo)) { + + return FALSE; + } + } + + // + // Reduce number of bits left to write and move remaining bits over + // + + cbits -= cbitsPart; + abits >>= cbitsPart; + } + + return TRUE; +} + + +// +// Internal Support Routine +// + +ULONG +MrcfFlushBitBuffer ( + PMRCF_BIT_IO BitIo + ) + +/*++ + +Routine Description: + + Write remaining bits to compressed data buffer + +Arguments: + + BitIo - Supplies a pointer to the bit buffer statics + +Return Value: + + ULONG - Returns total count of bytes written to the compressed data + buffer since the last call to MrcfSetBitBuffer(). Returns 0 if + overran buffer + +--*/ + +{ + DbgAssert((BitIo->cbitsBB) >= 0); + DbgAssert((BitIo->cbitsBB) <= 16); + + // + // Move bits to the compressed data buffer + // + + while ((BitIo->cbitsBB) > 0) { + + // + // Process low and high half. + // Check if output buffer is out of room + // + + if ((BitIo->cbBB) == 0) { return 0; } + + // + // Store a byte, adjust the count, get high half, nd adjust + // count of bits remaining + // + + *(BitIo->pbBB)++ = (UCHAR)((BitIo->abitsBB) & 0xFF); + (BitIo->cbBB)--; + (BitIo->abitsBB) >>= 8; + (BitIo->cbitsBB) -= 8; + } + + // + // Reset bit buffer, "abitsBB >>= 8" guarantees abitsBB is clear + // + + DbgAssert((BitIo->abitsBB) == 0); + + (BitIo->cbitsBB) = 0; + + return (BitIo->cbBBInitial)-(BitIo->cbBB); +} + diff --git a/private/utils/ufat/src/pch.cxx b/private/utils/ufat/src/pch.cxx new file mode 100644 index 000000000..39dee60df --- /dev/null +++ b/private/utils/ufat/src/pch.cxx @@ -0,0 +1,36 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + pch.cxx + +Abstract: + + This module is a precompiled header for ufat. + +Author: + + Matthew Bradburn (mattbr) 01-Feb-1994 + +--*/ + +#define _NTAPI_ULIB_ +#define _UFAT_MEMBER_ + +#include "ulib.hxx" +#include "ufat.hxx" + +#include "cluster.hxx" +#include "eaheader.hxx" +#include "easet.hxx" +#include "fat.hxx" +#include "fatdent.hxx" +#include "fatdir.hxx" +#include "fatsa.hxx" +#include "fatvol.hxx" +#include "filedir.hxx" +#include "reloclus.hxx" +#include "rfatsa.hxx" +#include "rootdir.hxx" diff --git a/private/utils/ufat/src/reloclus.cxx b/private/utils/ufat/src/reloclus.cxx new file mode 100644 index 000000000..79e79cc35 --- /dev/null +++ b/private/utils/ufat/src/reloclus.cxx @@ -0,0 +1,124 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + reloclus.cxx + +Abstract: + + This module contains the definition of the RELOCATION_CLUSTER class. + +Author: + + Ramon J. San Andres (ramonsa) 05-Nov-1991 + + +--*/ + +#include <pch.cxx> + + + +DEFINE_CONSTRUCTOR( RELOCATION_CLUSTER, OBJECT ); + +DEFINE_CAST_MEMBER_FUNCTION( RELOCATION_CLUSTER ); + + + + +RELOCATION_CLUSTER::~RELOCATION_CLUSTER ( + ) + +/*++ + +Routine Description: + + Destructor for the RELOCATION_CLUSTER object + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ +} + + + +BOOLEAN +RELOCATION_CLUSTER::Initialize ( + IN USHORT Cluster + ) +/*++ + +Routine Description: + + Initializes a relocation cluster + +Arguments: + + Cluster - Supplies the cluster number + +Return Value: + + BOOLEAN - TRUE + +--*/ +{ + _Cluster = Cluster; + + return TRUE; +} + + + + +LONG +RELOCATION_CLUSTER::Compare ( + IN PCOBJECT Object + ) CONST +/*++ + +Routine Description: + + Compares this relocation cluster against another object + +Arguments: + + Object - Supplies pointer to other object + +Return Value: + + LONG - See Compare in OBJECT + +--*/ +{ + PRELOCATION_CLUSTER OtherCluster; + + DebugPtrAssert( Object ); + + // + // If the other object is a relocation cluster, we do the comparison, + // otherwise we let someone else do it. + // + if ( OtherCluster = RELOCATION_CLUSTER::Cast( Object ) ) { + + if ( _Cluster < OtherCluster->QueryClusterNumber() ) { + return -1; + } else if ( _Cluster == OtherCluster->QueryClusterNumber() ) { + return 0; + } else { + return 1; + } + } else { + + return OBJECT::Compare( Object ); + } +} diff --git a/private/utils/ufat/src/rfatsa.cxx b/private/utils/ufat/src/rfatsa.cxx new file mode 100644 index 000000000..c3eeca7ba --- /dev/null +++ b/private/utils/ufat/src/rfatsa.cxx @@ -0,0 +1,1789 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + fatsa.cxx + +Author: + + Mark Shavlik (marks) 27-Mar-90 + Norbert Kusters (norbertk) 15-Jan-91 + +Environment: + + ULIB, User Mode + +--*/ + +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "cmem.hxx" +#include "error.hxx" +#include "rtmsg.h" +#include "drive.hxx" +#include "bpb.hxx" +#include "bitvect.hxx" + +extern "C" { + #include <stdio.h> +} + +extern UCHAR FatBootCode[512]; + +#if !defined(_AUTOCHECK_) && !defined(_SETUP_LOADER_) +#include "timeinfo.hxx" +#endif + + +// Control-C handling is not necessary for autocheck. +#if !defined( _AUTOCHECK_ ) && !defined(_SETUP_LOADER_) + +#include "keyboard.hxx" + +#endif + + +#define CSEC_FAT32MEG 65536 +#define CSEC_FAT16BIT 32680 + +#define MIN_CLUS_BIG 4085 // Minimum clusters for a big FAT. +#define MAX_CLUS_BIG 65525 // Maximum + 1 clusters for big FAT. + +#define sigSUPERSEC1 (UCHAR)0x55 // signature first byte +#define sigSUPERSEC2 (UCHAR)0xAA // signature second byte + + +DEFINE_EXPORTED_CONSTRUCTOR( REAL_FAT_SA, FAT_SA, UFAT_EXPORT ); + +BOOLEAN +REAL_FAT_SA::DosSaInit( + IN OUT PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN SECTORCOUNT NumberOfSectors, + IN OUT PMESSAGE Message + ) +{ + if (!SUPERAREA::Initialize(Mem, Drive, NumberOfSectors, Message)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + _sector_sig = (UCHAR *)SECRUN::GetBuf() + 510; + + return TRUE; +} + + +BOOLEAN +REAL_FAT_SA::DosSaSetBpb( + ) +{ +#if defined _SETUP_LOADER_ + return FALSE; +#else + ULONG Sec32Meg; // num sectors in 32mb + + DebugAssert(_drive); + DebugAssert(_drive->QuerySectors().GetHighPart() == 0); + DebugAssert(_drive->QueryHiddenSectors().GetHighPart() == 0); + + _sector_zero.Bpb.BytesPerSector = (USHORT)_drive->QuerySectorSize(); + + Sec32Meg = (32<<20) / _drive->QuerySectorSize(); + + if (_drive->QuerySectors() >= Sec32Meg) { + // >= 32Mb -- set BPB for large partition + + _sector_zero.Bpb.Sectors = 0; + _sector_zero.Bpb.LargeSectors = _drive->QuerySectors().GetLowPart(); + } else { + // Size of DOS0 partition is < 32Mb + _sector_zero.Bpb.Sectors = (USHORT)_drive->QuerySectors().GetLowPart(); + _sector_zero.Bpb.LargeSectors = 0; + } + _sector_zero.Bpb.Media = _drive->QueryMediaByte(); + _sector_zero.Bpb.SectorsPerTrack = (USHORT)_drive->QuerySectorsPerTrack(); + _sector_zero.Bpb.Heads = (USHORT)_drive->QueryHeads(); + _sector_zero.Bpb.HiddenSectors = _drive->QueryHiddenSectors().GetLowPart(); + + return TRUE; +#endif // _SETUP_LOADER_ +} + +VOID +REAL_FAT_SA::Construct ( + ) +/*++ + +Routine Description: + + Constructor for FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _fat = NULL; + _dir = NULL; + _StartDataLbn = 0; + _ClusterCount = 0; + _sysid = SYSID_NONE; +} + + +UFAT_EXPORT +REAL_FAT_SA::~REAL_FAT_SA( + ) +/*++ + +Routine Description: + + Destructor for REAL_FAT_SA. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +REAL_FAT_SA::Initialize( + IN OUT PLOG_IO_DP_DRIVE Drive, + IN OUT PMESSAGE Message, + IN BOOLEAN Formatted + ) +/*++ + +Routine Description: + + This routine initializes the FAT super area to an initial state. It + does so by first reading in the boot sector and verifying it with + the methods of DOS_SUPERAREA. Upon computing the super area's actual size, + the underlying SECRUN will be set to the correct size. + + If the super area does not already exist on disk, then the other Init + function should be called. + +Arguments: + + Drive - Supplies the drive where the super area resides. + Message - Supplies an outlet for messages + Formatted - Supplies a boolean which indicates whether or not + the volume is formatted. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECTORCOUNT sectors; + CONT_MEM cmem; + SECTORCOUNT reserved; + SECTORCOUNT sec_per_fat; + ULONG num_fats; + SECTORCOUNT sec_per_root; + ULONG root_entries; + ULONG sector_size; + + Destroy(); + + _sec_per_boot = max(1, BYTES_PER_BOOT_SECTOR/Drive->QuerySectorSize()); + + if (!Formatted) { + return _mem.Initialize() && + DosSaInit(&_mem, Drive, _sec_per_boot, Message); + } + + if (!Drive || + !(sector_size = Drive->QuerySectorSize()) || + !_mem.Initialize() || + !DosSaInit(&_mem, Drive, _sec_per_boot, Message) || + !SECRUN::Read()) { + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + Destroy(); + return FALSE; + } + + UnpackExtendedBios(&_sector_zero, + (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf()); + + if (!VerifyBootSector() || !_sector_zero.Bpb.Fats) { + Destroy(); + return FALSE; + } + + reserved = _sector_zero.Bpb.ReservedSectors; + sec_per_fat = _sector_zero.Bpb.SectorsPerFat; + num_fats = _sector_zero.Bpb.Fats; + root_entries = _sector_zero.Bpb.RootEntries; + sec_per_root = (root_entries*BytesPerDirent - 1)/sector_size + 1; + + _StartDataLbn = ComputeStartDataLbn(); + + sectors = QueryVirtualSectors() - _StartDataLbn; + _ClusterCount = (USHORT) (sectors/QuerySectorsPerCluster() + + FirstDiskCluster); + + _ft = (_ClusterCount >= 4087) ? LARGE : SMALL; + + if (_ft == SMALL) { + _sysid = SYSID_FAT12BIT; + } else if (QueryVirtualSectors() < CSEC_FAT32MEG) { + _sysid = SYSID_FAT16BIT; + } else { + _sysid = SYSID_FAT32MEG; + } + + if (!_mem.Initialize() || + !DosSaInit(&_mem, Drive, _StartDataLbn, Message)) { + Destroy(); + return FALSE; + } + + if (!(_dir = NEW ROOTDIR)) { + Destroy(); + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!cmem.Initialize((PCHAR) SECRUN::GetBuf() + + (reserved + sec_per_fat*num_fats)*sector_size, + sec_per_root*sector_size) || + !_dir->Initialize(&cmem, Drive, reserved + sec_per_fat*num_fats, + root_entries)) { + Destroy(); + return FALSE; + } + + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::CreateBootSector( + IN ULONG ClusterSize + ) +/*++ + +Routine Description: + + This routine updates fields in sector 0. + +Arguments: + + ClusterSize - Supplies the desired number of bytes per cluster. + +Return Value: + + TRUE - Success. + FALSE - Failure. + +--*/ +{ +#if defined _SETUP_LOADER_ + + return FALSE; + +#else + + SetVolId(ComputeVolId()); + + return SetBpb(ClusterSize) && + SetBootCode() && + SetPhysicalDriveType(_drive->IsRemovable() ? + PHYS_REMOVABLE : PHYS_FIXED) && + SetOemData() && + SetSignature(); + +#endif // _SETUP_LOADER_ +} + +BOOLEAN +REAL_FAT_SA::SetBpb( + IN ULONG ClusterSize + ) +/*++ + +Routine Description: + + This routine sets the BPB for the FAT file system. + +Arguments: + + ClusterSize - Supplies the desired number of bytes per cluster. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _SETUP_LOADER_ ) + + return FALSE; + +#else // _SETUP_LOADER_ + + SECTORCOUNT sectors; + ULONG sector_size; + USHORT sec_per_clus, alt_spc; + + if (!DosSaSetBpb()) { + DebugPrint("Could not do a DOS_SUPERAREA::SetBpb.\n"); + return FALSE; + } + + DebugAssert(_drive->QuerySectors().GetHighPart() == 0); + + sectors = _drive->QuerySectors().GetLowPart(); + sector_size = _drive->QuerySectorSize(); + + _ft = ComputeFatType(); + + _sector_zero.Bpb.RootEntries = ComputeRootEntries(); + sec_per_clus = ComputeSecClus(sectors, _ft, _drive->QueryMediaType()); + + if (sec_per_clus > MaxSecPerClus) { + DebugAssert("Disk too large\n"); + return FALSE; + } + + alt_spc = (USHORT)(ClusterSize / sector_size); + if (alt_spc > MaxSecPerClus) { + alt_spc = MaxSecPerClus; + } + + if (alt_spc > sec_per_clus) { + sec_per_clus = alt_spc; + } + + _sector_zero.Bpb.SectorsPerCluster = (UCHAR) sec_per_clus; + _sector_zero.Bpb.ReservedSectors = (USHORT)_sec_per_boot; + _sector_zero.Bpb.Fats = 2; + + // Formula for computing sectors per fat borrowed from fdisk. + if (_ft == SMALL) { + _sector_zero.Bpb.SectorsPerFat = (USHORT) (sectors/ + (2 + sector_size*sec_per_clus*2/3)); + } else { + _sector_zero.Bpb.SectorsPerFat = (USHORT) (sectors/ + (2 + sector_size*sec_per_clus/2)); + } + _sector_zero.Bpb.SectorsPerFat++; + + if (_ft == SMALL) { + memcpy(_sector_zero.SystemIdText, "FAT12 ", cSYSID); + } else { + memcpy(_sector_zero.SystemIdText, "FAT16 ", cSYSID); + } + + memcpy(_sector_zero.Label, "NO NAME ", cLABEL); + + _sector_zero.CurrentHead = 0; + + return TRUE; + +#endif // _SETUP_LOADER_ +} + +BOOLEAN +REAL_FAT_SA::SetBpb( + ) +/*++ + +Routine Description: + + This routine sets the BPB for the FAT file system. + +Arguments: + + None: + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + return SetBpb(0); +} + +BOOLEAN +REAL_FAT_SA::Create( + IN PCNUMBER_SET BadSectors, + IN OUT PMESSAGE Message, + IN PCWSTRING Label, + IN ULONG ClusterSize, + IN ULONG VirtualSize + ) +/*++ + +Routine Description: + + This routine initializes the FAT file system. + +Arguments: + + BadSectors - Supplies a list of the bad sectors on the volume. + Message - Supplies an outlet for messages. + Label - Supplies an optional label. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _SETUP_LOADER_ ) + + return FALSE; + +#else + + USHORT sector_size; + SECTORCOUNT sec_per_root; + CONT_MEM cmem; + HMEM hmem; + SECRUN secrun; + SECRUN small_secrun; + PUSHORT p; + USHORT cluster_count; + ULONG cluster_size; + LBN lbn; + ULONG i; + DSTRING label; + USHORT free_count; + USHORT bad_count; + PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK + SourceBootSector, TargetBootSector; + ULONG BootCodeOffset; + ULONG allocation_size; + + + if (!CreateBootSector(ClusterSize)) { + return FALSE; + } + + // calculate the actual cluster size + allocation_size = _sector_zero.Bpb.SectorsPerCluster * + _drive->QuerySectorSize(); + if (ClusterSize && allocation_size != ClusterSize) { + // Issue a warning to the user + Message->Set(MSG_FMT_ALLOCATION_SIZE_CHANGED); + Message->Display("%d", allocation_size); + } + + if (!_drive || + !(sector_size = (USHORT) _drive->QuerySectorSize()) || + (_sysid = ComputeSystemId()) == SYSID_NONE) { + return FALSE; + } + + sec_per_root = (_sector_zero.Bpb.RootEntries*BytesPerDirent - 1)/ + sector_size + 1; + + _StartDataLbn = _sector_zero.Bpb.ReservedSectors + + _sector_zero.Bpb.Fats*_sector_zero.Bpb.SectorsPerFat + + sec_per_root; + + if (_drive->QuerySectors().GetHighPart() != 0) { + // This should be checked before calling this procedure. + DebugAbort("Number of sectors exceeds 32 bits"); + return FALSE; + } + + _ClusterCount = (USHORT) (FirstDiskCluster + + (_drive->QuerySectors().GetLowPart() - _StartDataLbn)/ + QuerySectorsPerCluster()); + + if (!_mem.Initialize() || + !DosSaInit(&_mem, _drive, _StartDataLbn, Message)) { + return FALSE; + } + + // Zero fill the super area. + memset(_mem.GetBuf(), 0, (UINT) _mem.QuerySize()); + + // + // Make sure the disk is not write-protected. + // + + if (!_drive->Write(0, 1, _mem.GetBuf())) { + if (_drive->QueryLastNtStatus() == STATUS_MEDIA_WRITE_PROTECTED) { + Message->Set(MSG_FMT_WRITE_PROTECTED_MEDIA); + } else { + Message->Set(MSG_UNUSABLE_DISK); + } + Message->Display(""); + return FALSE; + } + + // Create the super area. + if (!CreateBootSector(ClusterSize)) { + return FALSE; + } + + if (!SetSystemId()) { + Message->Set(MSG_WRITE_PARTITION_TABLE); + Message->Display(""); + return FALSE; + } + + if (!cmem.Initialize((PCHAR) SECRUN::GetBuf() + + _sector_zero.Bpb.ReservedSectors*sector_size, + _sector_zero.Bpb.SectorsPerFat*sector_size)) { + return FALSE; + } + + // These "Hidden Status" messages are a hack to allow WinDisk to + // cancel a quick format, which ordinarily doesn't send any status + // messages, but which might take a while and for which there is a + // cancel button. When using format.com, no message will be displayed + // for this. + + Message->Set(MSG_HIDDEN_STATUS, NORMAL_MESSAGE, 0); + if (!Message->Display()) { + return FALSE; + } + + // Create the FAT. + DELETE(_fat); + if (!(_fat = NEW FAT)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!_fat->Initialize(&cmem, _drive, _sector_zero.Bpb.ReservedSectors, + _ClusterCount)) { + return FALSE; + } + + if (!cmem.Initialize((PCHAR) SECRUN::GetBuf() + + (_sector_zero.Bpb.ReservedSectors + + _sector_zero.Bpb.Fats*_sector_zero.Bpb.SectorsPerFat)* + sector_size, sec_per_root*sector_size)) { + return FALSE; + } + + DELETE(_dir); + if (!(_dir = NEW ROOTDIR)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!_dir->Initialize(&cmem, _drive, _sector_zero.Bpb.ReservedSectors + + _sector_zero.Bpb.Fats*_sector_zero.Bpb.SectorsPerFat, + _sector_zero.Bpb.RootEntries)) { + return FALSE; + } + + _fat->SetEarlyEntries(_sector_zero.Bpb.Media); + + Message->Set(MSG_HIDDEN_STATUS, NORMAL_MESSAGE, 0); + if (!Message->Display()) { + return FALSE; + } + + for (i = 0; i < BadSectors->QueryCardinality(); i++) { + if ((lbn = BadSectors->QueryNumber(i).GetLowPart()) < _StartDataLbn) { + Message->Set(MSG_UNUSABLE_DISK); + Message->Display(""); + return FALSE; + } else { + _fat->SetClusterBad((USHORT) ((lbn - _StartDataLbn)/ + QuerySectorsPerCluster()) + + FirstDiskCluster ); + } + } + + Message->Set(MSG_FORMAT_COMPLETE); + Message->Display(""); + + if (_drive->QueryMediaType() != F5_160_512 && + _drive->QueryMediaType() != F5_320_512) { + + if (Label) { + if (!label.Initialize(Label)) { + return FALSE; + } + } else { + switch (_drive->QueryRecommendedMediaType()) { + case F5_360_512: + case F5_320_512: + case F5_180_512: + case F5_160_512: + // These disk drives are lame and can't + // take the spin down without a verify + // so don't prompt for the label. + // This will avoid FORMAT failing. + + label.Initialize(); + break; + + default: + Message->Set(MSG_VOLUME_LABEL_PROMPT); + Message->Display(""); + Message->QueryStringInput(&label); + break; + + } + } + + while (!SetLabel(&label)) { + + Message->Set(MSG_INVALID_LABEL_CHARACTERS); + Message->Display(""); + + Message->Set(MSG_VOLUME_LABEL_PROMPT); + Message->Display(""); + Message->QueryStringInput(&label); + } + } + + // Copy the boot code into the secrun's buffer. + // This is complicated by the fact that DOS_SA::Write + // packs the data from the unpacked boot sector into + // the packed boot sector, so we have to set the + // first few fields in the unpacked version. + // + SourceBootSector = (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)FatBootCode; + + CopyUchar2(&_sector_zero.BootStrapJumpOffset, + SourceBootSector->BootStrapJumpOffset); + CopyUchar1(&_sector_zero.IntelNearJumpCommand, + SourceBootSector->IntelNearJumpCommand); + + // Copy the remainder of the boot code directly into + // the secrun. + // + TargetBootSector = (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf(); + + BootCodeOffset = FIELD_OFFSET( PACKED_EXTENDED_BIOS_PARAMETER_BLOCK, + StartBootCode ); + + memcpy( (PUCHAR)TargetBootSector + BootCodeOffset, + (PUCHAR)SourceBootSector + BootCodeOffset, + sizeof( FatBootCode ) - BootCodeOffset ); + + // Finally, write the changes to disk. + // + if (!Write(Message)) { + if (_drive->QueryLastNtStatus() == STATUS_MEDIA_WRITE_PROTECTED) { + Message->Set(MSG_FMT_WRITE_PROTECTED_MEDIA); + } else { + Message->Set(MSG_UNUSABLE_DISK); + } + Message->Display(""); + return FALSE; + } + + // Print an informative report. + // + cluster_count = QueryClusterCount() - FirstDiskCluster; + cluster_size = sector_size*QuerySectorsPerCluster(); + + Message->Set(MSG_TOTAL_DISK_SPACE); + Message->Display("%9u", cluster_count*cluster_size); + + if (bad_count = _fat->QueryBadClusters()) { + Message->Set(MSG_BAD_SECTORS); + Message->Display("%9u", bad_count*cluster_size); + } + + free_count = _fat->QueryFreeClusters(); + + Message->Set(MSG_AVAILABLE_DISK_SPACE); + Message->Display("%9u", free_count*cluster_size); + + Message->Set(MSG_ALLOCATION_UNIT_SIZE); + Message->Display("%9u", cluster_size); + + Message->Set(MSG_AVAILABLE_ALLOCATION_UNITS); + Message->Display("%9u", free_count); + + if (QueryVolId()) { + Message->Set(MSG_BLANK_LINE); + Message->Display(); + p = (PUSHORT) &_sector_zero.SerialNumber; + Message->Set(MSG_VOLUME_SERIAL_NUMBER); + Message->Display("%04X%04X", p[1], p[0]); + } + + return TRUE; + +#endif +} + + +BOOLEAN +REAL_FAT_SA::RecoverFile( + IN PCWSTRING FullPathFileName, + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine runs through the clusters for the file described by + 'FileName' and takes out bad sectors. + +Arguments: + + FullPathFileName - Supplies a full path name of the file to recover. + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ +#if defined( _SETUP_LOADER_ ) + + return FALSE; + +#else // _SETUP_LOADER_ + + + HMEM hmem; + USHORT clus; + BOOLEAN changes; + PFATDIR fatdir; + BOOLEAN need_delete; + FAT_DIRENT dirent; + ULONG old_file_size; + ULONG new_file_size; + + if ((clus = QueryFileStartingCluster(FullPathFileName, + &hmem, + &fatdir, + &need_delete, + &dirent)) == 1) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", FullPathFileName); + return FALSE; + } + + if (clus == 0xFFFF) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (clus == 0) { + Message->Set(MSG_FILE_NOT_FOUND); + Message->Display("%W", FullPathFileName); + return FALSE; + } + + if (dirent.IsDirectory()) { + old_file_size = _drive->QuerySectorSize()* + QuerySectorsPerCluster()* + _fat->QueryLengthOfChain(clus); + } else { + old_file_size = dirent.QueryFileSize(); + } + + if (!RecoverChain(&clus, &changes)) { + Message->Set(MSG_CHK_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (dirent.IsDirectory() || changes) { + new_file_size = _drive->QuerySectorSize()* + QuerySectorsPerCluster()* + _fat->QueryLengthOfChain(clus); + } else { + new_file_size = old_file_size; + } + + if (changes) { + + +// Autochk doesn't need control C handling. +#if !defined( _AUTOCHECK_ ) + + // Disable contol-C handling and + + if (!KEYBOARD::EnableBreakHandling()) { + Message->Set(MSG_CANT_LOCK_THE_DRIVE); + Message->Display(""); + return FALSE; + } + +#endif + + + // Lock the drive in preparation for writes. + + if (!_drive->Lock()) { + Message->Set(MSG_CANT_LOCK_THE_DRIVE); + Message->Display(""); + return FALSE; + } + + dirent.SetStartingCluster(clus); + + dirent.SetFileSize(new_file_size); + + if (!fatdir->Write()) { + return FALSE; + } + + if (!Write(Message)) { + return FALSE; + } + + +// Autochk doesn't need control C handling. +#if !defined( _AUTOCHECK_ ) + + KEYBOARD::DisableBreakHandling(); + +#endif + + + } + + Message->Set(MSG_RECOV_BYTES_RECOVERED); + Message->Display("%d%d", new_file_size, old_file_size); + + + if (need_delete) { + DELETE(fatdir); + } + + return TRUE; + +#endif // _SETUP_LOADER_ +} + + +UFAT_EXPORT +BOOLEAN +REAL_FAT_SA::Read( + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine reads the super area. It will succeed if it can + read the boot sector, the root directory, and at least one of + the FATs. + + If the position of the internal FAT has not yet been determined, + this routine will attempt to map it to a readable FAT on the disk. + +Arguments: + + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECRUN secrun; + CONT_MEM cmem; + LBN fat_lbn; + ULONG sector_size; + PCHAR fat_pos; + SECTORCOUNT sec_per_fat; + SECTORCOUNT num_res; + ULONG i; + PFAT fat; + + if (!SECRUN::Read()) { + + // Check to see if super area was allocated as formatted. + if (QueryLength() <= _sec_per_boot) { + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + // Check the boot sector. + if (!secrun.Initialize(&_mem, _drive, 0, _sec_per_boot) || + !secrun.Read()) { + UnpackExtendedBios(&_sector_zero, + (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf()); + Message->Set(MSG_CANT_READ_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + // Check the root directory. + if (!_dir || !_dir->Read()) { + Message->Set(MSG_BAD_DIR_READ); + Message->Display(""); + return FALSE; + } + + // Check for one good FAT. + if (_fat) { + if (!_fat->Read()) { + Message->Set(MSG_DISK_ERROR_READING_FAT); + Message->Display("%d", 1 + + (_fat->QueryStartLbn() - _sector_zero.Bpb.ReservedSectors)/ + _sector_zero.Bpb.SectorsPerFat); + return FALSE; + } else { + Message->Set(MSG_SOME_FATS_UNREADABLE); + Message->Display(""); + } + } else { + sector_size = _drive->QuerySectorSize(); + num_res = _sector_zero.Bpb.ReservedSectors; + fat_pos = (PCHAR) SECRUN::GetBuf() + num_res*sector_size; + sec_per_fat = _sector_zero.Bpb.SectorsPerFat; + + for (i = 0; i < QueryFats(); i++) { + + fat_lbn = num_res + i*sec_per_fat; + if (!cmem.Initialize(fat_pos + i*sec_per_fat*sector_size, + sec_per_fat*sector_size)) { + return FALSE; + } + + if (!(fat = NEW FAT)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!fat->Initialize(&cmem, _drive, fat_lbn, _ClusterCount)) { + return FALSE; + } + + if (!fat->Read()) { + Message->Set(MSG_DISK_ERROR_READING_FAT); + Message->Display("%d", 1 + + (fat->QueryStartLbn() - _sector_zero.Bpb.ReservedSectors)/ + _sector_zero.Bpb.SectorsPerFat); + DELETE(fat); + } + + if (_fat) { + DELETE(fat); + } else { + _fat = fat; + } + + } + + if (!_fat) { + Message->Set(MSG_CANT_READ_ANY_FAT); + Message->Display(""); + return FALSE; + } + } + + } else { + + UnpackExtendedBios(&_sector_zero, + (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf()); + + if (!_fat && (QueryLength() > _sec_per_boot)) { + + fat_lbn = _sector_zero.Bpb.ReservedSectors; + sector_size = _drive->QuerySectorSize(); + + + if (!cmem.Initialize((PCHAR) SECRUN::GetBuf() + fat_lbn*sector_size, + QuerySectorsPerFat()*sector_size)) { + return FALSE; + } + + if (!(_fat = NEW FAT)) { + Message->Set(MSG_FMT_NO_MEMORY); + Message->Display(""); + return FALSE; + } + + if (!_fat->Initialize(&cmem, _drive, fat_lbn, _ClusterCount, + QuerySectorsPerFat())) { + return FALSE; + } + } + } + + return TRUE; +} + + +BOOLEAN +REAL_FAT_SA::Write( + IN OUT PMESSAGE Message + ) +/*++ + +Routine Description: + + This routine writes the super area. It will succeed if it can + write the boot sector, the root directory, and at least one of + the FATs. + + This routine will duplicate the working FAT to all other FATs + in the super area. + +Arguments: + + Message - Supplies an outlet for messages. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + SECRUN secrun; + + DupFats(); + + PackExtendedBios(&_sector_zero, + (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf()); + + if (!SECRUN::Write()) { + if (!_fat || !_dir) { + Message->Set(MSG_CANT_WRITE_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + PackExtendedBios(&_sector_zero, + (PPACKED_EXTENDED_BIOS_PARAMETER_BLOCK)SECRUN::GetBuf()); + + if (!secrun.Initialize(&_mem, _drive, 0, _sec_per_boot) || + !secrun.Write()) { + Message->Set(MSG_CANT_WRITE_BOOT_SECTOR); + Message->Display(""); + return FALSE; + } + + if (!_dir->Write()) { + Message->Set(MSG_CANT_WRITE_ROOT_DIR); + Message->Display(""); + return FALSE; + } + + if (!_fat->Write()) { + Message->Set(MSG_BAD_FAT_WRITE); + Message->Display(""); + return FALSE; + } else { + Message->Set(MSG_SOME_FATS_UNWRITABLE); + Message->Display(""); + } + } + + return TRUE; +} + + +UFAT_EXPORT +SECTORCOUNT +REAL_FAT_SA::QueryFreeSectors( + ) CONST +/*++ + +Routine Description: + + This routine computes the number of unused sectors on disk. + +Arguments: + + None. + +Return Value: + + The number of free sectors on disk. + +--*/ +{ + if (!_fat) { + perrstk->push(ERR_NOT_READ, QueryClassId()); + return 0; + } + + return _fat->QueryFreeClusters()*QuerySectorsPerCluster(); +} + + +FATTYPE +REAL_FAT_SA::QueryFatType( + ) CONST +/*++ + +Routine Description: + + This routine computes the FATTYPE of the FAT for this volume. + +Arguments: + + None. + +Return Value: + + The FATTYPE for the FAT. + +--*/ +{ + return _ft; +} + + +VOID +REAL_FAT_SA::Destroy( + ) +/*++ + +Routine Description: + + This routine cleans up the local data in the fat super area. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + DELETE(_fat); + DELETE(_dir); + + _StartDataLbn = 0; + _ClusterCount = 0; + _sysid = SYSID_NONE; +} + +BOOLEAN +REAL_FAT_SA::DupFats( + ) +/*++ + +Routine Description: + + This routine will duplicate the current FAT to all other FATs + in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + ULONG i; + PCHAR fat_pos; + ULONG fat_size; + ULONG sector_size; + SECTORCOUNT num_res; + SECTORCOUNT sec_per_fat; + + if (!_fat || !_drive || !(sector_size = _drive->QuerySectorSize())) { + return FALSE; + } + + num_res = _sector_zero.Bpb.ReservedSectors; + sec_per_fat = _sector_zero.Bpb.SectorsPerFat; + fat_size = sec_per_fat*sector_size; + fat_pos = (PCHAR) SECRUN::GetBuf() + num_res*sector_size; + + for (i = 0; i < QueryFats(); i++) { + if (num_res + i*sec_per_fat != _fat->QueryStartLbn()) { + memcpy(fat_pos + i*fat_size, _fat->GetBuf(), (UINT) fat_size); + } + } + + return TRUE; +} + + +LBN +REAL_FAT_SA::ComputeStartDataLbn( + ) CONST +/*++ + +Routine Description: + + This routine computes the first LBN of the data part of a FAT disk. + In other words, the LBN of cluster 2. + +Arguments: + + None. + +Return Value: + + The LBN of the start of data. + +--*/ +{ + return _sector_zero.Bpb.ReservedSectors + + _sector_zero.Bpb.Fats*_sector_zero.Bpb.SectorsPerFat + + (_sector_zero.Bpb.RootEntries*BytesPerDirent - 1)/ + _drive->QuerySectorSize() + 1; +} + + +#if !defined(_SETUP_LOADER_) + +USHORT +REAL_FAT_SA::ComputeRootEntries( + ) CONST +/*++ + +Routine Description: + + This routine uses the size of the disk and a standard table in + order to compute the required number of root directory entries. + +Arguments: + + None. + +Return Value: + + The required number of root directory entries. + +--*/ +{ + switch (_drive->QueryMediaType()) { + + case F3_720_512: + case F5_360_512: + case F5_320_512: + case F5_320_1024: + case F5_180_512: + case F5_160_512: + return 112; + + case F5_1Pt2_512: + case F3_1Pt44_512: + return 224; + + case F3_2Pt88_512: + case F3_20Pt8_512: + return 240; + } + + return 512; +} + + +USHORT +REAL_FAT_SA::ComputeSecClus( + IN SECTORCOUNT Sectors, + IN FATTYPE FatType, + IN MEDIA_TYPE MediaType + ) +/*++ + +Routine Description: + + This routine computes the number of sectors per cluster required + based on the actual number of sectors. + +Arguments: + + Sectors - Supplies the total number of sectors on the disk. + FatType - Supplies the type of FAT. + MediaType - Supplies the type of the media. + +Return Value: + + The required number of sectors per cluster. + +--*/ +{ + USHORT sec_per_clus; + SECTORCOUNT threshold; + + if (FatType == SMALL) { + threshold = MIN_CLUS_BIG; + sec_per_clus = 1; + } else { + threshold = MAX_CLUS_BIG; + sec_per_clus = 1; + } + + while (Sectors >= threshold) { + sec_per_clus *= 2; + threshold *= 2; + } + + switch (MediaType) { + + case F5_320_512: + case F5_360_512: + case F3_720_512: + case F3_2Pt88_512: + sec_per_clus = 2; + break; + + case F3_20Pt8_512: + sec_per_clus = 4; + break; + + default: + break; + + } + + return sec_per_clus; +} + +#endif // _SETUP_LOADER_ + + +BOOLEAN +REAL_FAT_SA::RecoverChain( + IN OUT PUSHORT StartingCluster, + OUT PBOOLEAN ChangesMade, + IN USHORT EndingCluster, + IN BOOLEAN Replace + ) +/*++ + +Routine Description: + + This routine will recover the chain beginning with 'StartingCluster' + in the following way. It will verify the readability of every cluster + until it reaches 'EndingCluster' or the end of the chain. If a cluster + is not readable then 'ChangesMade' will be set to TRUE, the FAT will + be marked to indicate that the cluster is bad, and the cluster will be + taken out of the chain. Additionally, if 'Replace' is set to TRUE, + the FAT will be scanned for a readable free cluster to replace the lost + ones with. Failure to accomplish this will result in a return value + of FALSE being returned. + + If the very first cluster of the chain was bad then then + 'StartingCluster' will be set with the new starting cluster of the + chain even if this starting cluster is past 'EndingCluster'. If the + chain is left empty then 'StartingCluster' will be set to zero. + +Arguments: + + StartingCluster - Supplies the first cluster of the chain to recover. + ChangesMade - Returns TRUE if changes to the chain were made. + EndingCluster - Supplies the final cluster to recover. + Replace - Supplies whether or not to replace bad clusters with + new ones. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + CONST max_transfer_bytes = 65536; + + HMEM hmem; + CLUSTER_CHAIN cluster; + USHORT clus, prev; + USHORT replacement; + BOOLEAN finished; + USHORT max_clusters; + USHORT chain_length; + USHORT i; + + DebugAssert(_fat); + DebugAssert(ChangesMade); + DebugAssert(StartingCluster); + + if (!hmem.Initialize()) { + return FALSE; + } + + *ChangesMade = FALSE; + finished = TRUE; + + max_clusters = (USHORT)(max_transfer_bytes/ + QuerySectorsPerCluster()/ + _drive->QuerySectorSize()); + + if (!max_clusters) { + max_clusters = 1; + } + + chain_length = _fat->QueryLengthOfChain(*StartingCluster); + + for (i = 0; i < chain_length; i += max_clusters) { + + if (!cluster.Initialize(&hmem, _drive, this, _fat, + _fat->QueryNthCluster(*StartingCluster, i), + min(max_clusters, chain_length - i))) { + + return FALSE; + } + + if (!cluster.Read()) { + + // Since the quick analysis detected some errors do the slow + // analysis to pinpoint them. + + finished = FALSE; + break; + } + } + + prev = 0; + clus = *StartingCluster; + + if (!clus) { + return TRUE; + } + + while (!finished) { + if (!cluster.Initialize(&hmem, _drive, this, _fat, clus, 1)) { + return FALSE; + } + + finished = (BOOLEAN) (_fat->IsEndOfChain(clus) || clus == EndingCluster); + + if (!cluster.Read()) { + + // There is a bad cluster so indicate that changes will be made. + + *ChangesMade = TRUE; + + + // Take the bad cluster out of the cluster chain. + + if (prev) { + _fat->SetEntry(prev, _fat->QueryEntry(clus)); + + _fat->SetClusterBad(clus); + + clus = prev; + } else { + *StartingCluster = _fat->IsEndOfChain(clus) ? 0 : + _fat->QueryEntry(clus); + + _fat->SetClusterBad(clus); + + clus = 0; + } + + + // If a replacement cluster is wanted then get one. + + if (Replace) { + + if (!(replacement = _fat->AllocChain(1))) { + return FALSE; + } + + + // Zero fill and write the replacement. + + cluster.Initialize(&hmem, _drive, this, _fat, replacement, 1); + memset(hmem.GetBuf(), 0, (UINT) hmem.QuerySize()); + cluster.Write(); + + + if (finished) { + EndingCluster = replacement; + finished = FALSE; + } + + + // Link in the replacement. + + if (prev) { + _fat->InsertChain(replacement, replacement, prev); + } else { + if (*StartingCluster) { + _fat->SetEntry(replacement, *StartingCluster); + } + *StartingCluster = replacement; + } + } + } + + prev = clus; + clus = clus ? _fat->QueryEntry(clus) : *StartingCluster; + } + + return TRUE; +} + +ULONG +REAL_FAT_SA::SecPerBoot() +{ + return _sec_per_boot; +} + +BOOLEAN +REAL_FAT_SA::SetBootCode( + ) +/*++ + +Routine Description: + + This routine sets the boot code in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + _sector_zero.IntelNearJumpCommand = 0xEB; + _sector_zero.BootStrapJumpOffset = 0x903C; + SetBootSignature(); + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::SetPhysicalDriveType( + IN PHYSTYPE PhysType + ) +/*++ + +Routine Description: + + This routine sets the physical drive type in the super area. + +Arguments: + + PhysType - Supplies the physical drive type. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + _sector_zero.PhysicalDrive = (UCHAR)PhysType; + return TRUE; +} + +INLINE +BOOLEAN +REAL_FAT_SA::SetOemData( + ) +/*++ + +Routine Description: + + This routine sets the OEM data in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + memcpy( (void*)_sector_zero.OemData, (void*)OEMTEXT, OEMTEXTLENGTH); + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::SetSignature( + ) +/*++ + +Routine Description: + + This routine sets the sector zero signature in the super area. + +Arguments: + + None. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + if (!_sector_sig) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + return FALSE; + } + + *_sector_sig = sigSUPERSEC1; + *(_sector_sig + 1) = sigSUPERSEC2; + + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::VerifyBootSector( + ) +/*++ + +Routine Description: + + This routine checks key parts of sector 0 to insure that the data + being examined is indeed a zero sector. + +Arguments: + + None. + +Return Value: + + FALSE - Invalid sector zero. + TRUE - Valid sector zero. + +--*/ +{ + PUCHAR p; + +// We don't check for 55 AA anymore because we have reason to +// believe that there are versions of FORMAT out there that +// don't put it down. + +#if 0 + if (!IsFormatted()) { + return FALSE; + } +#endif + + p = (PUCHAR) GetBuf(); + + return p[0] == 0xE9 || (p[0] == 0xEB && p[2] == 0x90); +} + +ULONG +REAL_FAT_SA::QuerySectorFromCluster( + IN ULONG Cluster, + OUT PUCHAR NumSectors + ) +{ + if (NULL != NumSectors) { + *NumSectors = (UCHAR)QuerySectorsPerCluster(); + } + + return (Cluster - FirstDiskCluster)*QuerySectorsPerCluster() + + QueryStartDataLbn(); +} + +BOOLEAN +REAL_FAT_SA::IsClusterCompressed( + IN ULONG + ) CONST +{ + return FALSE; +} + +VOID +REAL_FAT_SA::SetClusterCompressed( + IN ULONG, + IN BOOLEAN fCompressed + ) +{ + if (fCompressed) + DebugAssert("REAL_FAT_SA shouldn't have compressed clusters."); +} + +UCHAR +REAL_FAT_SA::QuerySectorsRequiredForPlainData( + IN ULONG + ) +{ + DebugAssert("REAL_FAT_SA didn't expect call to QuerySectorsRequiredForPlainData\n"); + return 0; + +} + +BOOLEAN +REAL_FAT_SA::VerifyFatExtensions( + FIX_LEVEL, PMESSAGE, PBOOLEAN + ) +{ + // + // We have no fat extensions, we're real. + // + + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::CheckSectorHeapAllocation( + FIX_LEVEL, PMESSAGE, PBOOLEAN + ) +{ + // + // We have no sector heap, we're real. + // + + return TRUE; +} + +BOOLEAN +REAL_FAT_SA::AllocateClusterData( + ULONG, UCHAR, BOOLEAN, UCHAR + ) +{ + DebugAbort("Didn't expect REAL_FAT_SA::AllocateClusterData to be " + "called."); + return FALSE; +} + +BOOLEAN +REAL_FAT_SA::FreeClusterData( + ULONG + ) +{ + DebugAbort("Didn't expect REAL_FAT_SA::FreeClusterData to be " + "called."); + return FALSE; +} diff --git a/private/utils/ufat/src/rootdir.cxx b/private/utils/ufat/src/rootdir.cxx new file mode 100644 index 000000000..219612bc7 --- /dev/null +++ b/private/utils/ufat/src/rootdir.cxx @@ -0,0 +1,133 @@ +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ufat.hxx" + +#include "error.hxx" + + +DEFINE_EXPORTED_CONSTRUCTOR( ROOTDIR, FATDIR, UFAT_EXPORT ); + +VOID +ROOTDIR::Construct ( + ) +/*++ + +Routine Description: + + Constructor for ROOTDIR. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _number_of_entries = 0; +} + + +UFAT_EXPORT +ROOTDIR::~ROOTDIR( + ) +/*++ + +Routine Description: + + Destructor for ROOTDIR. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + Destroy(); +} + + +UFAT_EXPORT +BOOLEAN +ROOTDIR::Initialize( + IN PMEM Mem, + IN OUT PLOG_IO_DP_DRIVE Drive, + IN LBN StartingSector, + IN LONG NumberOfEntries + ) +/*++ + +Routine Description: + + This routine initializes the ROOTDIR object by specifying a drive, + a position and a size. + +Arguments: + + Mem - Supplies the memory for the run of sectors. + Drive - Supplies the drive where the root directory is. + StartingSector - Supplies the starting sector of the root directory. + NumberOfEntries - Supplies the number of entries in the root directory. + +Return Value: + + FALSE - Failure. + TRUE - Success. + +--*/ +{ + LONG sector_size; + SECTORCOUNT n; + + Destroy(); + + if (!Drive || !(sector_size = Drive->QuerySectorSize())) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + _number_of_entries = NumberOfEntries; + + n = (BytesPerDirent*NumberOfEntries - 1)/sector_size + 1; + + if (!_secrun.Initialize(Mem, Drive, StartingSector, n)) { + perrstk->push(ERR_NOT_INIT, QueryClassId()); + Destroy(); + return FALSE; + } + + return TRUE; +} + + +VOID +ROOTDIR::Destroy( + ) +/*++ + +Routine Description: + + This routine returns the object to its initial state. Init must be + called for this routine to be useful again. This routine will + free up memory. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + _number_of_entries = 0; +} diff --git a/private/utils/ufat/src/sources b/private/utils/ufat/src/sources new file mode 100644 index 000000000..8442c6844 --- /dev/null +++ b/private/utils/ufat/src/sources @@ -0,0 +1,93 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in $(BASEDIR)\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=utils +MINORCOMP=ufat + +TARGETNAME=ufat +TARGETPATH=obj +TARGETTYPE=DYNLINK +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + ..\..\ulib\src\obj\*\ulib.lib \ + ..\..\ifsutil\src\obj\*\ifsutil.lib + +USE_CRTDLL=1 +BLDCRT=1 + +DLLENTRY=InitializeUfat + +PRECOMPILED_INCLUDE= pch.cxx + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=cluster.cxx \ + eaheader.cxx \ + easet.cxx \ + entry.cxx \ + fat.cxx \ + fatdent.cxx \ + fatdir.cxx \ + fatsa.cxx \ + fatsachk.cxx \ + fatsacnv.cxx \ + fatvol.cxx \ + filedir.cxx \ + reloclus.cxx \ + rfatsa.cxx \ + rootdir.cxx \ + ufat.cxx \ + ufat.rc + +DBLSPACE_SOURCES= \ + cvfexts.cxx \ + dblentry.cxx \ + fatdbsa.cxx \ + mrcf.c \ + fatdbvol.cxx + +INCLUDES=..\inc;..\..\ulib\inc;..\..\ifsutil\inc;$(BASEDIR)\public\sdk\inc + +!IF "$(NTDEBUG)" == "cvp" || "$(NTDEBUG)" == "ntsd" +!IFDEF NOMEMLEAK +C_DEFINES=-DCONDITION_HANDLING=1 -DDBG=1 -DUNICODE=1 +!ELSE +!IFDEF STACK_TRACE +C_DEFINES=-DCONDITION_HANDLING=1 -DDBG=1 -DMEMLEAK -DSTACK_TRACE -DUNICODE=1 +!ELSE +C_DEFINES=-DCONDITION_HANDLING=1 -DDBG=1 -DMEMLEAK -DUNICODE=1 +!ENDIF +!ENDIF +!ELSE # NTDEBUG +C_DEFINES=-DCONDITION_HANDLING=1 -DDBG=0 -DUNICODE=1 +!ENDIF # NTDEBUG + +CXXFLAGS=+d +UMLIBS=obj\*\ufat.lib + +UMTYPE=console + +UMTEST= +UMRES=obj\*\ufat.res + +DLLDEF=ufat.def diff --git a/private/utils/ufat/src/ufat.cxx b/private/utils/ufat/src/ufat.cxx new file mode 100644 index 000000000..6d79cba71 --- /dev/null +++ b/private/utils/ufat/src/ufat.cxx @@ -0,0 +1,167 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ufat.cxx + +Abstract: + + This module contains run-time, global support for the + FAT IFS Utilities library (UFAT). This support includes: + + - creation of CLASS_DESCRIPTORs + - Global objects + +Author: + + Bill McJohn (billmc) 30-May-1991 + +Environment: + + User Mode + +Notes: + +--*/ + +#include <pch.cxx> + +#define _UFAT_MEMBER_ +#include "ulib.hxx" +#include "ufat.hxx" + +#include "error.hxx" + +#if !defined( _AUTOCHECK_ ) + + ERRSTACK* perrstk; + +#endif // _AUTOCHECK_ + +// Local prototypes + +STATIC +BOOLEAN +DefineClassDescriptors( + ); + +extern "C" +UFAT_EXPORT +BOOLEAN +InitializeUfat ( + PVOID DllHandle, + ULONG Reason, + PCONTEXT Context + ); + +UFAT_EXPORT +BOOLEAN +InitializeUfat ( + PVOID DllHandle, + ULONG Reason, + PCONTEXT Context + ) +/*++ + +Routine Description: + + Initialize Ufat by constructing and initializing all + global objects. These include: + + - all CLASS_DESCRIPTORs (class_cd) + +Arguments: + + None. + +Return Value: + + BOOLEAN - Returns TRUE if all global objects were succesfully constructed + and initialized. + +--*/ + +{ + + STATIC BOOLEAN fInit = FALSE; + + if ( fInit ) { + + return( TRUE ); + } + + if ( DefineClassDescriptors() ) { + + fInit = TRUE; + return TRUE; + + } else { + + DebugAbort( "Ufat initialization failed!!!\n" ); + return( FALSE ); + } +} + + + +DECLARE_CLASS( CLUSTER_CHAIN ); +DECLARE_CLASS( EA_HEADER ); +DECLARE_CLASS( EA_SET ); +DECLARE_CLASS( FAT ); +DECLARE_CLASS( FATDIR ); +DECLARE_CLASS( FAT_DIRENT ); +DECLARE_CLASS( FAT_SA ); +DECLARE_CLASS( FAT_VOL ); +#ifdef DBLSPACE_ENABLED +DECLARE_CLASS( FATDB_VOL ); +#endif // DBLSPACE_ENABLED +DECLARE_CLASS( FILEDIR ); +DECLARE_CLASS( ROOTDIR ); +DECLARE_CLASS( RELOCATION_CLUSTER ); +#ifdef DBLSPACE_ENABLED +DECLARE_CLASS( CVF_FAT_EXTENS ); +#endif // DBLSPACE_ENABLED +DECLARE_CLASS( REAL_FAT_SA ); +#ifdef DBLSPACE_ENABLED +DECLARE_CLASS( FATDB_SA ); +#endif // DBLSPACE_ENABLED + + +STATIC +BOOLEAN +DefineClassDescriptors( + ) +{ + if( DEFINE_CLASS_DESCRIPTOR( CLUSTER_CHAIN ) && +#ifdef DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( CVF_FAT_EXTENS ) && +#endif // DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( EA_HEADER ) && + DEFINE_CLASS_DESCRIPTOR( EA_SET ) && + DEFINE_CLASS_DESCRIPTOR( FAT ) && +#ifdef DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( FATDB_SA ) && +#endif // DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( FATDIR ) && + DEFINE_CLASS_DESCRIPTOR( FAT_DIRENT ) && + DEFINE_CLASS_DESCRIPTOR( FAT_SA ) && + DEFINE_CLASS_DESCRIPTOR( FAT_VOL ) && +#ifdef DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( FATDB_VOL ) && +#endif // DBLSPACE_ENABLED + DEFINE_CLASS_DESCRIPTOR( FILEDIR ) && + DEFINE_CLASS_DESCRIPTOR( RELOCATION_CLUSTER ) && + DEFINE_CLASS_DESCRIPTOR( REAL_FAT_SA ) && + DEFINE_CLASS_DESCRIPTOR( ROOTDIR ) + ) { + + return TRUE; + + } else { + + DebugPrint( "Could not initialize class descriptors!"); + return FALSE; + } +} diff --git a/private/utils/ufat/src/ufat.def b/private/utils/ufat/src/ufat.def new file mode 100644 index 000000000..94c50360e --- /dev/null +++ b/private/utils/ufat/src/ufat.def @@ -0,0 +1,10 @@ +LIBRARY UFAT + +DESCRIPTION File System Utilities for FAT + +DATA NONSHARED + +EXPORTS + Chkdsk + Format + Recover diff --git a/private/utils/ufat/src/ufat.rc b/private/utils/ufat/src/ufat.rc new file mode 100644 index 000000000..de5186a47 --- /dev/null +++ b/private/utils/ufat/src/ufat.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "FAT Utility DLL" +#define VER_INTERNALNAME_STR "UFAT" +#define VER_ORIGINALFILENAME_STR "UFAT.DLL" + +#include "common.ver" diff --git a/private/utils/ufat/src/ufatalph.ded b/private/utils/ufat/src/ufatalph.ded new file mode 100644 index 000000000..53226b4e4 --- /dev/null +++ b/private/utils/ufat/src/ufatalph.ded @@ -0,0 +1,9 @@ + FatDbCreate + FatDbDelete + FatDbFormat + FatDbChkdsk + ??0FATDB_VOL@@QAA@XZ + ??1FATDB_VOL@@UAA@XZ + ?Initialize@FATDB_VOL@@QAAEPBVWSTRING@@0PAVMESSAGE@@E@Z + MrcfDecompress + ?SetCvfSectorCount@FATDB_SA@@QAAEK@Z diff --git a/private/utils/ufat/src/ufatmips.ded b/private/utils/ufat/src/ufatmips.ded new file mode 100644 index 000000000..4fc3aa9ba --- /dev/null +++ b/private/utils/ufat/src/ufatmips.ded @@ -0,0 +1,9 @@ + FatDbCreate + FatDbDelete + FatDbFormat + FatDbChkdsk + ??0FATDB_VOL@@QAA@XZ + ??1FATDB_VOL@@UAA@XZ + ?SetCvfSectorCount@FATDB_SA@@QAAEK@Z + ?Initialize@FATDB_VOL@@QAAEPBVWSTRING@@0PAVMESSAGE@@E@Z + MrcfDecompress diff --git a/private/utils/ufat/src/ufatppc.ded b/private/utils/ufat/src/ufatppc.ded new file mode 100644 index 000000000..4fc3aa9ba --- /dev/null +++ b/private/utils/ufat/src/ufatppc.ded @@ -0,0 +1,9 @@ + FatDbCreate + FatDbDelete + FatDbFormat + FatDbChkdsk + ??0FATDB_VOL@@QAA@XZ + ??1FATDB_VOL@@UAA@XZ + ?SetCvfSectorCount@FATDB_SA@@QAAEK@Z + ?Initialize@FATDB_VOL@@QAAEPBVWSTRING@@0PAVMESSAGE@@E@Z + MrcfDecompress diff --git a/private/utils/ufat/src/ufatx86.ded b/private/utils/ufat/src/ufatx86.ded new file mode 100644 index 000000000..5aceb73fd --- /dev/null +++ b/private/utils/ufat/src/ufatx86.ded @@ -0,0 +1,9 @@ + FatDbCreate + FatDbDelete + FatDbFormat + FatDbChkdsk + ??0FATDB_VOL@@QAE@XZ + ??1FATDB_VOL@@UAE@XZ + ?Initialize@FATDB_VOL@@QAEEPBVWSTRING@@0PAVMESSAGE@@E@Z + ?SetCvfSectorCount@FATDB_SA@@QAEEK@Z + _MrcfDecompress@20 |