summaryrefslogtreecommitdiffstats
path: root/private/utils/ufat
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/utils/ufat/dirs16
-rw-r--r--private/utils/ufat/inc/cluster.hxx178
-rw-r--r--private/utils/ufat/inc/cvf.hxx251
-rw-r--r--private/utils/ufat/inc/cvfexts.hxx294
-rw-r--r--private/utils/ufat/inc/dblentry.hxx68
-rw-r--r--private/utils/ufat/inc/eaheader.hxx229
-rw-r--r--private/utils/ufat/inc/easet.hxx243
-rw-r--r--private/utils/ufat/inc/fat.hxx904
-rw-r--r--private/utils/ufat/inc/fatdbsa.hxx789
-rw-r--r--private/utils/ufat/inc/fatdbvol.hxx97
-rw-r--r--private/utils/ufat/inc/fatdent.hxx1052
-rw-r--r--private/utils/ufat/inc/fatdir.hxx90
-rw-r--r--private/utils/ufat/inc/fatsa.hxx754
-rw-r--r--private/utils/ufat/inc/fatvol.hxx151
-rw-r--r--private/utils/ufat/inc/filedir.hxx213
-rw-r--r--private/utils/ufat/inc/mrcf.h99
-rw-r--r--private/utils/ufat/inc/reloclus.hxx87
-rw-r--r--private/utils/ufat/inc/rfatsa.hxx986
-rw-r--r--private/utils/ufat/inc/rootdir.hxx173
-rw-r--r--private/utils/ufat/inc/ufat.hxx36
-rw-r--r--private/utils/ufat/src/cluster.cxx523
-rw-r--r--private/utils/ufat/src/cvfexts.cxx129
-rw-r--r--private/utils/ufat/src/dblentry.cxx1177
-rw-r--r--private/utils/ufat/src/eaheader.cxx215
-rw-r--r--private/utils/ufat/src/easet.cxx404
-rw-r--r--private/utils/ufat/src/entry.cxx496
-rw-r--r--private/utils/ufat/src/fat.cxx1024
-rw-r--r--private/utils/ufat/src/fatdbsa.cxx1593
-rw-r--r--private/utils/ufat/src/fatdbvol.cxx139
-rw-r--r--private/utils/ufat/src/fatdent.cxx1125
-rw-r--r--private/utils/ufat/src/fatdir.cxx201
-rw-r--r--private/utils/ufat/src/fatsa.cxx866
-rw-r--r--private/utils/ufat/src/fatsachk.cxx3246
-rw-r--r--private/utils/ufat/src/fatsacnv.cxx814
-rw-r--r--private/utils/ufat/src/fatvol.cxx435
-rw-r--r--private/utils/ufat/src/filedir.cxx132
-rw-r--r--private/utils/ufat/src/makefile6
-rw-r--r--private/utils/ufat/src/makefile.inc14
-rw-r--r--private/utils/ufat/src/mrcf.c1726
-rw-r--r--private/utils/ufat/src/pch.cxx36
-rw-r--r--private/utils/ufat/src/reloclus.cxx124
-rw-r--r--private/utils/ufat/src/rfatsa.cxx1789
-rw-r--r--private/utils/ufat/src/rootdir.cxx133
-rw-r--r--private/utils/ufat/src/sources93
-rw-r--r--private/utils/ufat/src/ufat.cxx167
-rw-r--r--private/utils/ufat/src/ufat.def10
-rw-r--r--private/utils/ufat/src/ufat.rc10
-rw-r--r--private/utils/ufat/src/ufatalph.ded9
-rw-r--r--private/utils/ufat/src/ufatmips.ded9
-rw-r--r--private/utils/ufat/src/ufatppc.ded9
-rw-r--r--private/utils/ufat/src/ufatx86.ded9
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", &current_file, num_blocks);
+ all_contig = FALSE;
+ }
+
+ }
+
+ } else {
+ Message->Set(MSG_FILE_NOT_FOUND);
+ Message->Display("%W", &current_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