Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1495 lines
40 KiB

#include <ntreppch.h>
#pragma hdrstop
#include <frs.h>
#include <tablefcn.h>
#include <perrepsr.h>
#define INITGUID
#include "frstrace.h"
/////////////////////////////////FROM MAIN.c ////////////////////////////////////
PCHAR LatestChanges[] = {
"Latest changes:",
" RC3-Q1: 432553, 436070, 432549",
" WMI Perf Tracing",
" Allow all junction points",
" Automatic trigger of non-auth restore on WRAP_ERROR",
" Allow changing replica root path",
" 03/18/00 - sudarc - checkin",
" 03/15/00 - 32/64 Comm fix.",
" 03/20 - merge with sudarc.",
" 03/30/00 - sudarc - checkin - volatile connection cleanup.",
" 04/14/00 - sudarc - checkin - bugbug, memleak, and poll summary eventlog.",
NULL
};
HANDLE ShutDownEvent;
HANDLE ShutDownComplete;
HANDLE DataBaseEvent;
HANDLE JournalEvent;
HANDLE ChgOrdEvent;
HANDLE ReplicaEvent;
HANDLE CommEvent;
HANDLE DsPollEvent;
HANDLE DsShutDownComplete;
PWCHAR ServerPrincName;
BOOL IsAMember = FALSE;
BOOL IsADc = FALSE;
BOOL IsAPrimaryDc = FALSE;
BOOL EventLogIsRunning = FALSE;
BOOL RpcssIsRunning = FALSE;
BOOL RunningAsAService = TRUE;
BOOL NoDs = FALSE;
BOOL FrsIsShuttingDown = FALSE;
BOOL FrsIsAsserting = FALSE;
//
// Require mutual authentication
//
BOOL MutualAuthenticationIsEnabled;
BOOL MutualAuthenticationIsEnabledAndRequired;
//
// Directory and file filter lists from registry.
//
PWCHAR RegistryFileExclFilterList;
PWCHAR RegistryFileInclFilterList;
PWCHAR RegistryDirExclFilterList;
PWCHAR RegistryDirInclFilterList;
//
// Synchronize the shutdown thread with the service controller
//
CRITICAL_SECTION ServiceLock;
//
// Synchronize initialization
//
CRITICAL_SECTION MainInitLock;
//
// Convert the ANSI ArgV into a UNICODE ArgV
//
PWCHAR *WideArgV;
//
// Process Handle
//
HANDLE ProcessHandle;
//
// Working path / DB Log path
//
PWCHAR WorkingPath;
PWCHAR DbLogPath;
//
// Database directories (UNICODE and ASCII)
//
PWCHAR JetPath;
PWCHAR JetFile;
PWCHAR JetFileCompact;
PWCHAR JetSys;
PWCHAR JetTemp;
PWCHAR JetLog;
PCHAR JetPathA;
PCHAR JetFileA;
PCHAR JetFileCompactA;
PCHAR JetSysA;
PCHAR JetTempA;
PCHAR JetLogA;
//
// Limit the amount of staging area used (in kilobytes). This is
// a soft limit, the actual usage may be higher.
//
DWORD StagingLimitInKb;
//
// Default staging limit in kb to be assigned to a new staging area.
//
DWORD DefaultStagingLimitInKb;
//
// Max number replica sets and Jet Sessions allowed.
//
ULONG MaxNumberReplicaSets;
ULONG MaxNumberJetSessions;
//
// Maximum number of outbound changeorders allowed outstanding per connection.
//
ULONG MaxOutLogCoQuota;
//
// If TRUE then try to preserve existing file OIDs whenever possible.
// -- See Bug 352250 for why this is a risky thing to do.
//
BOOL PreserveFileOID;
//
// Limits on how many time and for how long we will continue to retry a
// change order when the parent is missing.
//
ULONG MaxCoRetryTimeoutMinutes;
ULONG MaxCoRetryTimeoutCount;
//
// Major/minor (see frs.h)
//
ULONG NtFrsMajor = NTFRS_MAJOR;
ULONG NtFrsMinor = NTFRS_MINOR;
ULONG NtFrsStageMajor = NTFRS_STAGE_MAJOR;
ULONG NtFrsStageMinor = NTFRS_STAGE_MINOR_1;
ULONG NtFrsCommMinor = NTFRS_COMM_MINOR_3;
PCHAR NtFrsModule = __FILE__;
PCHAR NtFrsDate = __DATE__;
PCHAR NtFrsTime = __TIME__;
//
// Shutdown timeout
//
ULONG ShutDownTimeOut = DEFAULT_SHUTDOWN_TIMEOUT;
//
// A useful thing to have around
//
WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 2];
PWCHAR ComputerDnsName;
PWCHAR ServiceLongName;
//
// The rpc server may reference this table as soon as the rpc interface
// is registered. Make sure it is setup.
//
extern PGEN_TABLE ReplicasByGuid;
//
// The staging area table is references early in the startup process
//
extern PGEN_TABLE StagingAreaTable;
PGEN_TABLE CompressionTable;
//
// Size of buffer to use when enumerating directories. Actual memory
// usage will be #levels * SizeOfBuffer.
//
LONG EnumerateDirectorySizeInBytes;
BOOL MainInitHasRun;
//
// Do not accept stop control unless the service is in SERVICE_RUNNING state.
// This prevents the service from getting confused when a stop is called
// while the service is starting.
//
SERVICE_STATUS ServiceStatus = {
SERVICE_WIN32_OWN_PROCESS,
SERVICE_START_PENDING,
// SERVICE_ACCEPT_STOP |
// SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN,
0,
0,
0,
60*1000
};
//
// Supported compression formats.
//
//
// This is the compression format for uncompressed data.
//
DEFINE_GUID ( /* 00000000-0000-0000-0000-000000000000 */
FrsGuidCompressionFormatNone,
0x00000000,
0x0000,
0x0000,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
);
//
// This is the compression format for data compressed using NTFS's LZNT1 compression
// routines.
//
DEFINE_GUID ( /* 64d2f7d2-2695-436d-8830-8d3c58701e15 */
FrsGuidCompressionFormatLZNT1,
0x64d2f7d2,
0x2695,
0x436d,
0x88, 0x30, 0x8d, 0x3c, 0x58, 0x70, 0x1e, 0x15
);
//
// Fixed guid for the dummy cxtion (aka GhostCxtion) assigned to orphan remote
// change orders whose inbound cxtion has been deleted from the DS but they
// have already past the fetching state and can finish without the real cxtion
// coming back. No authentication checks are made for this dummy cxtion.
//
DEFINE_GUID ( /* b9d307a7-a140-4405-991e-281033f03309 */
FrsGuidGhostCxtion,
0xb9d307a7,
0xa140,
0x4405,
0x99, 0x1e, 0x28, 0x10, 0x33, 0xf0, 0x33, 0x09
);
DEFINE_GUID ( /* 3fe2820f-3045-4932-97fe-00d10b746dbf */
FrsGhostJoinGuid,
0x3fe2820f,
0x3045,
0x4932,
0x97, 0xfe, 0x00, 0xd1, 0x0b, 0x74, 0x6d, 0xbf
);
//
// Static Ghost cxtion structure. This cxtion is assigned to orphan remote change
// orders in the inbound log whose cxtion is deleted from the DS but who have already
// past the fetching state and do not need the cxtion to complete processing. No
// authentication checks are made for this dummy cxtion.
//
PCXTION FrsGhostCxtion;
SERVICE_STATUS_HANDLE ServiceStatusHandle = NULL;
VOID
InitializeEventLog(
VOID
);
DWORD
FrsSetServiceFailureAction(
VOID
);
VOID
FrsRpcInitializeAccessChecks(
VOID
);
BOOL
FrsSetupPrivileges (
VOID
);
VOID
CfgRegAdjustTuningDefaults(
VOID
);
VOID
CommInitializeCommSubsystem(
VOID
);
VOID
SndCsInitialize(
VOID
);
// FRS Capacity Planning
//
#define RESOURCE_NAME L"MofResourceName"
#define IMAGE_PATH L"ntfrs.exe"
DWORD FrsWmiEventTraceFlag = FALSE;
TRACEHANDLE FrsWmiTraceRegistrationHandle = (TRACEHANDLE) 0;
TRACEHANDLE FrsWmiTraceLoggerHandle = (TRACEHANDLE) 0;
// This is the FRS control Guid for the group of Guids traced below
//
DEFINE_GUID ( /* 78a8f0b1-290e-4c4c-9720-c7f1ef68ce21 */
FrsControlGuid,
0x78a8f0b1,
0x290e,
0x4c4c,
0x97, 0x20, 0xc7, 0xf1, 0xef, 0x68, 0xce, 0x21
);
// Traceable Guids start here
//
DEFINE_GUID ( /* 2eee6bbf-6665-44cf-8ed7-ceea1d306085 */
FrsTransGuid,
0x2eee6bbf,
0x6665,
0x44cf,
0x8e, 0xd7, 0xce, 0xea, 0x1d, 0x30, 0x60, 0x85
);
TRACE_GUID_REGISTRATION FrsTraceGuids[] =
{
{ & FrsTransGuid, NULL }
};
#define FrsGuidCount (sizeof(FrsTraceGuids) / sizeof(TRACE_GUID_REGISTRATION))
//
// Trace initialization / shutdown routines
//
VOID
MainInit(
VOID
)
/*++
Routine Description:
Initialize anything necessary to run the service
Arguments:
None.
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "MainInit:"
EnterCriticalSection(&MainInitLock);
//
// No need to initialize twice
//
if (MainInitHasRun) {
LeaveCriticalSection(&MainInitLock);
return;
}
//
// SETUP THE INFRASTRUCTURE
//
// DEBUG_INIT();
//
// Fetch the staging file limit
//
CfgRegReadDWord(FKC_STAGING_LIMIT, NULL, 0, &StagingLimitInKb);
DPRINT1(4, ":S: Staging limit is: %d KB\n", StagingLimitInKb);
//
// Put the default value in registry if there is no key present.
//
CfgRegWriteDWord(FKC_STAGING_LIMIT,
NULL,
FRS_RKF_FORCE_DEFAULT_VALUE | FRS_RKF_KEEP_EXISTING_VALUE,
0);
//
// Get Max number of replica sets allowed.
//
CfgRegReadDWord(FKC_MAX_NUMBER_REPLICA_SETS, NULL, 0, &MaxNumberReplicaSets);
//
// Get outstanding CO qutoa limit on outbound connections.
//
CfgRegReadDWord(FKC_OUT_LOG_CO_QUOTA, NULL, 0, &MaxOutLogCoQuota);
//
// Get boolean to tell us to preserve file object IDs
// -- See Bug 352250 for why this is a risky thing to do.
CfgRegReadDWord(FKC_PRESERVE_FILE_OID, NULL, 0, &PreserveFileOID);
//
// Get the service long name for use in error messages.
//
ServiceLongName = FrsGetResourceStr(IDS_SERVICE_LONG_NAME);
//
// Initialize the Delayed command server. This command server
// is really a timeout queue that the other command servers use
// to retry or check the state of previously issued commands that
// have an indeterminate completion time.
//
// WARN: MUST BE FIRST -- Some command servers may use this
// command server during their initialization.
//
WaitInitialize();
FrsDelCsInitialize();
//
// SETUP THE COMM LAYER
//
//
// Initialize the low level comm subsystem
//
CommInitializeCommSubsystem();
//
// Initialize the Send command server. The receive command server
// starts when we register our RPC interface.
//
SndCsInitialize();
//
// SETUP THE SUPPORT COMMAND SERVERS
//
//
// Staging file fetcher
//
FrsFetchCsInitialize();
//
// Staging file installer
//
FrsInstallCsInitialize();
//
// Staging file generator
//
FrsStageCsInitialize();
//
// outbound log processor
//
OutLogInitialize();
//
// LAST, KICK OFF REPLICATION
//
//
// MUST PRECEED DATABASE AND DS INITIALIZATION
//
// The DS command server and the Database command server depend on
// the replica control initialization.
//
// Initialize the replica control command server
//
RcsInitializeReplicaCmdServer();
//
// Actually, we can start the database at any time after the delayed
// command server and the replica control command server. But its
// a good idea to fail sooner than later to make cleanup more predictable.
//
DbsInitialize();
//
// Free up memory by reducing our working set size
//
SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
MainInitHasRun = TRUE;
LeaveCriticalSection(&MainInitLock);
}
////////////////////////////////////FROM MAIN.C////////////////////////////////////
#define FREE(_p) \
if (_p) free(_p);
PWCHAR *
MainConvertArgV(
DWORD ArgC,
PCHAR *ArgV
)
/*++
Routine Description:
Convert short char ArgV into wide char ArgV
Arguments:
ArgC - From main
ArgV - From main
Return Value:
Address of the new ArgV
--*/
{
#undef DEBSUB
#define DEBSUB "MainConvertArgV:"
PWCHAR *wideArgV;
wideArgV = (PWCHAR*)malloc((ArgC + 1) * sizeof(PWCHAR));
wideArgV[ArgC] = NULL;
while (ArgC-- >= 1) {
wideArgV[ArgC] = (PWCHAR)malloc((strlen(ArgV[ArgC]) + 1) * sizeof(WCHAR));
wsprintf(wideArgV[ArgC], L"%hs", ArgV[ArgC]);
if (wideArgV[ArgC]) {
_wcslwr(wideArgV[ArgC]);
}
}
return wideArgV;
}
#define STAGEING_IOSIZE (64 * 1024)
DWORD
FrsGetReparseTag(
IN HANDLE Handle,
OUT ULONG *ReparseTag
);
BOOL CompressionEnabled = TRUE;
//
// Local data structures
//
//
// The compressed chunk header is the structure that starts every
// new chunk in the compressed data stream. In our definition here
// we union it with a ushort to make setting and retrieving the chunk
// header easier. The header stores the size of the compressed chunk,
// its signature, and if the data stored in the chunk is compressed or
// not.
//
// Compressed Chunk Size:
//
// The actual size of a compressed chunk ranges from 4 bytes (2 byte
// header, 1 flag byte, and 1 literal byte) to 4098 bytes (2 byte
// header, and 4096 bytes of uncompressed data). The size is encoded
// in a 12 bit field biased by 3. A value of 1 corresponds to a chunk
// size of 4, 2 => 5, ..., 4095 => 4098. A value of zero is special
// because it denotes the ending chunk header.
//
// Chunk Signature:
//
// The only valid signature value is 3. This denotes a 4KB uncompressed
// chunk using with the 4/12 to 12/4 sliding offset/length encoding.
//
// Is Chunk Compressed:
//
// If the data in the chunk is compressed this field is 1 otherwise
// the data is uncompressed and this field is 0.
//
// The ending chunk header in a compressed buffer contains the a value of
// zero (space permitting).
//
typedef union _COMPRESSED_CHUNK_HEADER {
struct {
USHORT CompressedChunkSizeMinus3 : 12;
USHORT ChunkSignature : 3;
USHORT IsChunkCompressed : 1;
} Chunk;
USHORT Short;
} COMPRESSED_CHUNK_HEADER, *PCOMPRESSED_CHUNK_HEADER;
#define FRS_MAX_CHUNKS_TOUNCOMPRESS 16
DWORD
StuNewGenerateStage(
PWCHAR SrcFile,
PWCHAR DestFile
)
/*++
Routine Description:
Create and populate the staging file. Currently there are four cases
of interest based on the state of Coe, FromPreExisting and Md5:
Coe FromPreExisting Md5
NULL FALSE NULL Fetch on demand or outlog trimmed so stage file must be regenerated
NULL FALSE non-null Fetch of pre-existing file by downstream partner. check MD5.
NULL TRUE NULL doesn't occur
NULL TRUE non-null doesn't occur
non-NULL FALSE NULL Generate stage file for local CO
non-NULL FALSE non-null doesn't occur -- MD5 only generated for preexisting files
non-NULL TRUE NULL doesn't occur -- MD5 always generated for preexisting files.
non-NULL TRUE non-null Generate stage file from pre-existing file and send MD5 upstream to check for a match.
Arguments:
Coc -- ptr to change order command. NULL on incoming fetch requests from downstream partners.
Coe -- ptr to change order entry. NULL when regenerating the staging file for fetch
FromPreExisting -- TRUE if this staging file is being generated from a
preexisting file.
Md5 -- Generate the MD5 digest for the caller and return it if Non-NULL
SizeOfFileGenerated - Valid when the size generated is needed, otherwise NULL
Return Value:
WIN32 STATUS
--*/
{
#undef DEBSUB
#define DEBSUB "StuNewGenerateStage:"
OVERLAPPED OpLockOverLap;
LONGLONG StreamBytesLeft;
LONG BuffBytesLeft;
DWORD WStatus;
DWORD NumBackupDataBytes;
ULONG ReparseTag;
ULONG OpenOptions;
WORD OldSecurityControl;
WORD NewSecurityControl;
WORD *SecurityControl;
BOOL FirstBuffer = TRUE;
BOOL Regenerating = FALSE;
BOOL SkipCo = FALSE;
BOOL FirstOpen = TRUE;
BOOL StartOfStream = TRUE;
PWCHAR StagePath = NULL;
PWCHAR FinalPath = NULL;
PUCHAR BackupBuf = NULL;
PVOID BackupContext = NULL;
HANDLE OpLockEvent = NULL;
HANDLE SrcHandle = INVALID_HANDLE_VALUE;
HANDLE StageHandle = INVALID_HANDLE_VALUE;
HANDLE OpLockHandle = INVALID_HANDLE_VALUE;
WIN32_STREAM_ID *StreamId;
PSTAGE_HEADER Header = NULL;
STAGE_HEADER StageHeaderMemory;
ULONG Length;
PREPLICA NewReplica = NULL;
WCHAR TStr[100];
DWORD NtStatus;
PUCHAR CompressedBuf = NULL;
DWORD CompressedSize;
PVOID WorkSpace = NULL;
DWORD WorkSpaceSize = 0;
DWORD FragmentWorkSpaceSize = 0;
DWORD UnCompressedFileSize = 0;
DWORD CompressedFileSize = 0;
OpenOptions = OPEN_OPTIONS;
//
// The header is located at the beginning of the newly created staging file
//
// Fill in the header with info from the src file
// Compression type
// Change order
// Attributes
//
Header = &StageHeaderMemory;
ZeroMemory(Header, sizeof(STAGE_HEADER));
Header->Attributes.FileAttributes = GetFileAttributes(SrcFile);
RETRY_OPEN:
WStatus = FrsOpenSourceFileW(&SrcHandle,
SrcFile,
READ_ACCESS,
OpenOptions);
if (!WIN_SUCCESS(WStatus)) {
goto out;
}
//
// What type of reparse is it?
//
if (FirstOpen &&
(Header->Attributes.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
FirstOpen = FALSE;
//
// reparse tag
//
WStatus = FrsGetReparseTag(SrcHandle, &ReparseTag);
if (!WIN_SUCCESS(WStatus)) {
goto out;
}
//
// We only accept operations on files with SIS and HSM reparse points.
// For example a rename of a SIS file into a replica tree needs to prop
// a create CO.
//
if ((ReparseTag != IO_REPARSE_TAG_HSM) &&
(ReparseTag != IO_REPARSE_TAG_SIS)) {
WIN_SET_FAIL(WStatus);
goto out;
}
//
// We hit a file with a known reparse tag type.
// Close and reopen the file without the FILE_OPEN_REPARSE_POINT
// option so backup read will get the underlying data.
//
FRS_CLOSE(SrcHandle);
ClearFlag(OpenOptions, FILE_OPEN_REPARSE_POINT);
goto RETRY_OPEN;
}
//
// Assume retriable errors for the silly boolean functions
//
WIN_SET_RETRY(WStatus);
//
// Default to no compression if we can't get the compression state
//
if (!FrsGetCompression(SrcFile, SrcHandle, &Header->Compression)) {
Header->Compression = COMPRESSION_FORMAT_NONE;
}
//
// The backup data begins at the first 32 byte boundary following the header
//
Header->DataLow = QuadQuadAlignSize(sizeof(STAGE_HEADER));
//
// Major/minor
//
Header->Major = NtFrsStageMajor;
Header->Minor = NtFrsStageMinor;
//
// Create the local staging name
//
StagePath = FrsWcsDup(DestFile);
//
// Create the staging file
//
WStatus = StuCreateFile(StagePath,&StageHandle);
if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(StageHandle)) {
goto out;
}
//
// Approximate size of the staging file
//
WStatus = FrsSetFilePointer(StagePath, StageHandle,
Header->Attributes.EndOfFile.HighPart,
Header->Attributes.EndOfFile.LowPart);
if(!WIN_SUCCESS(WStatus)) {
goto out;
}
WStatus = FrsSetEndOfFile(StagePath, StageHandle);
if(!WIN_SUCCESS(WStatus)) {
goto out;
}
//
// Rewind the file, write the header, and set the file pointer
// to the next 32 byte boundary
//
WStatus = FrsSetFilePointer(StagePath, StageHandle, 0, 0);
if(!WIN_SUCCESS(WStatus)) {
goto out;
}
WStatus = StuWriteFile(StagePath, StageHandle, Header, sizeof(STAGE_HEADER));
if(!WIN_SUCCESS(WStatus)) {
goto out;
}
WStatus = FrsSetFilePointer(StagePath, StageHandle, 0, Header->DataLow);
if(!WIN_SUCCESS(WStatus)) {
goto out;
}
UnCompressedFileSize = Header->DataLow;
CompressedFileSize = Header->DataLow;
//
// Backup the src file into the staging file
//
BackupBuf = FrsAlloc(STAGEING_IOSIZE);
CompressedBuf = FrsAlloc(STAGEING_IOSIZE * 2);
StreamBytesLeft = 0;
NtStatus = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1,
&WorkSpaceSize,
&FragmentWorkSpaceSize);
WStatus = FrsSetLastNTError(NtStatus);
if (!WIN_SUCCESS(WStatus)) {
goto out;
}
// printf("WorkSpaceSize = %d, FragmentWorkSpaceSize = %d\n", WorkSpaceSize, FragmentWorkSpaceSize);
WorkSpace = FrsAlloc(WorkSpaceSize);
while (TRUE) {
//
// read source
//
if (!BackupRead(SrcHandle,
BackupBuf,
STAGEING_IOSIZE,
&NumBackupDataBytes,
FALSE,
TRUE,
&BackupContext)) {
goto out;
}
//
// No more data; Backup done
//
if (NumBackupDataBytes == 0) {
break;
}
UnCompressedFileSize += NumBackupDataBytes;
//
// write the staging file
//
NtStatus = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, // compression engine
BackupBuf, // input
NumBackupDataBytes, // length of input
CompressedBuf, // output
STAGEING_IOSIZE * 2, // length of output
4096, // chunking that occurs in buffer
&CompressedSize, // result size
WorkSpace); // I have no clue
//
// STATUS_BUFFER_ALL_ZEROS means the compression worked without a hitch
// and in addition the input buffer was all zeros.
//
if (NtStatus == STATUS_BUFFER_ALL_ZEROS) {
NtStatus = STATUS_SUCCESS;
}
WStatus = FrsSetLastNTError(NtStatus);
// printf("Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
if (!WIN_SUCCESS(WStatus)) {
printf("Error : Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
goto out;
}
// printf("Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
CompressedFileSize += CompressedSize;
WStatus = StuWriteFile(StagePath, StageHandle, CompressedBuf, CompressedSize);
if (!WIN_SUCCESS(WStatus)) {
// if (!StuWriteFile(StagePath, StageHandle, BackupBuf, NumBackupDataBytes)) {
goto out;
}
}
//
// Release handles as soon as possible
//
FRS_CLOSE(SrcHandle);
//
// Make sure all of the data is on disk. We don't want to lose
// it across reboots
//
WStatus = FrsFlushFile(StagePath, StageHandle);
if (!WIN_SUCCESS(WStatus)) {
goto out;
}
//
// Done with the staging file handle
//
if (BackupContext) {
BackupRead(StageHandle, NULL, 0, NULL, TRUE, TRUE, &BackupContext);
}
FRS_CLOSE(StageHandle);
BackupContext = NULL;
printf("%ws Orig= %d, Comp= %d, Percentage_Comp= %5.2f\n", SrcFile, UnCompressedFileSize, CompressedFileSize,
((UnCompressedFileSize - CompressedFileSize)/(float)UnCompressedFileSize) * 100);
WStatus = ERROR_SUCCESS;
out:
//
// Release resources
//
FRS_CLOSE(SrcHandle);
if (BackupContext) {
BackupRead(StageHandle, NULL, 0, NULL, TRUE, TRUE, &BackupContext);
}
FRS_CLOSE(StageHandle);
FrsFree(BackupBuf);
FrsFree(CompressedBuf);
FrsFree(WorkSpace);
FrsFree(StagePath);
FrsFree(FinalPath);
return WStatus;
}
ULONG
StuNewExecuteInstall(
IN PWCHAR SrcFile,
IN PWCHAR DestFile
)
/*++
Routine Description:
Install a staging file by restoring it to a temporary file in the
same directory as the file to be replaced and then renaming it
to its final destination.
Arguments:
Coe
Return Value:
Win32 status -
ERROR_SUCCESS - All installed or aborted. Don't retry.
ERROR_GEN_FAILURE - Couldn't install bag it.
ERROR_SHARING_VIOLATION - Couldn't open the target file. retry later.
ERROR_DISK_FULL - Couldn't allocate the target file. retry later.
ERROR_HANDLE_DISK_FULL - ?? retry later.
--*/
{
#undef DEBSUB
#define DEBSUB "StuNewExecuteInstall:"
DWORD WStatus;
DWORD BytesRead;
ULONG Restored;
ULONG ToRestore;
ULONG High;
ULONG Low;
ULONG Flags;
BOOL AttributeMissmatch;
ULONG CreateDisposition;
ULONG OpenOptions;
BOOL IsDir;
BOOL IsReparsePoint;
ULONG SizeHigh;
ULONG SizeLow;
BOOL ExistingOid;
PVOID RestoreContext = NULL;
PWCHAR StagePath = NULL;
PSTAGE_HEADER Header = NULL;
HANDLE DstHandle = INVALID_HANDLE_VALUE;
HANDLE StageHandle = INVALID_HANDLE_VALUE;
PUCHAR RestoreBuf = NULL;
FILE_OBJECTID_BUFFER FileObjID;
STAGE_HEADER StageHeaderMemory;
DWORD NtStatus;
PUCHAR UnCompressedBuf = NULL;
DWORD UnCompressedBufLen = 0;
DWORD ActUnCompressedSize = 0;
COMPRESSED_CHUNK_HEADER ChunkHeader;
DWORD RestoreBufIndex = 0;
DWORD RestoreBufSize = 0;
LONG LenOfPartialChunk = 0;
DWORD NoOfChunks = 0;
//
// PROCESS STAGING FILE
//
StagePath = FrsWcsDup(SrcFile);
//
// Open the stage file for shared, sequential reads
//
WStatus = StuOpenFile(StagePath, GENERIC_READ,&StageHandle);
if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(StageHandle)) {
goto CLEANUP;
}
//
// Read the header
//
Header = &StageHeaderMemory;
ZeroMemory(Header, sizeof(STAGE_HEADER));
WStatus = StuReadFile(StagePath, StageHandle, Header, sizeof(STAGE_HEADER), &BytesRead);
if (!WIN_SUCCESS(WStatus)) {
printf("Can't read file %ws. Error %d\n", StagePath, WStatus);
}
//
// Don't understand this header format
//
if (Header->Major != NtFrsStageMajor) {
printf("Stage Header Major Version (%d) not supported. Current Service Version is %d\n",
Header->Major, NtFrsStageMajor);
goto CLEANUP;
}
//
// Minor version NTFRS_STAGE_MINOR_1 had the change order extension in the
// header.
//
/* if (Header->Minor >= NTFRS_STAGE_MINOR_0) {
} else {
//
// This is an older stage file. No CO Extension in the header.
//
Header->ChangeOrderCommand.Extension = NULL;
}
*/
//
// PROCESS TEMPORARY FILE
//
IsDir = Header->Attributes.FileAttributes & FILE_ATTRIBUTE_DIRECTORY;
IsReparsePoint = Header->Attributes.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT;
CreateDisposition = FILE_OPEN;
if (!IsDir) {
//
// In case this is an HSM file don't force the data to be read from
// tape since the remote co is just going to overwrite all the data anyway.
//
// Setting CreateDisposition to FILE_OVERWRITE seems to cause a regression
// Failure with an ACL Test where we set a deny all ACL and then the
// open fails. This is a mystery for now so don't do it.
// In addtion overwrite fails if RO attribute is set on file.
//
//CreateDisposition = FILE_OVERWRITE;
printf("Target is a file\n");
} else {
printf("Target is a directory\n");
}
//
// In case this is a SIS or HSM file open the underlying file not the
// reparse point. For HSM, need to clear FILE_OPEN_NO_RECALL to write it.
//
OpenOptions = OPEN_OPTIONS;
WStatus = StuCreateFile(DestFile,&DstHandle);
if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(DstHandle)) {
goto CLEANUP;
}
//
// Truncate the file if not a directory
//
if (!IsDir && !SetEndOfFile(DstHandle)) {
printf("++ WARN - SetEndOfFile(%ws);", DestFile, GetLastError());
}
//
// For the silly functions that don't return a win status
//
WIN_SET_FAIL(WStatus);
//
// Set compression mode
//
WStatus = FrsSetCompression(DestFile, DstHandle, Header->Compression);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
//
// Set attributes
//
WStatus = FrsSetFileAttributes(DestFile,
DstHandle,
Header->Attributes.FileAttributes &
~NOREPL_ATTRIBUTES);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
//
// Seek to the first byte of data in the stage file
//
WStatus = FrsSetFilePointer(StagePath, StageHandle,
Header->DataHigh, Header->DataLow);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
//
// Restore the stage file into the temporary file
//
RestoreBuf = FrsAlloc(STAGEING_IOSIZE);
UnCompressedBuf = 0;
UnCompressedBufLen = 0;
do {
//
// read stage
//
WStatus = StuReadFile(StagePath, StageHandle, RestoreBuf, STAGEING_IOSIZE, &ToRestore);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
if (ToRestore == 0) {
break;
}
RestoreBufIndex = 0;
RestoreBufSize = 0;
NoOfChunks = 0;
while ((RestoreBufIndex <= ToRestore) && (NoOfChunks < FRS_MAX_CHUNKS_TOUNCOMPRESS)) {
memcpy(&ChunkHeader, RestoreBuf + RestoreBufIndex,sizeof(COMPRESSED_CHUNK_HEADER));
// printf("Chunck size is 0x%x\n", ChunkHeader.Chunk.CompressedChunkSizeMinus3);
RestoreBufSize = RestoreBufIndex;
++NoOfChunks;
RestoreBufIndex+=ChunkHeader.Chunk.CompressedChunkSizeMinus3+3;
}
//
// Check if the uncompressed buffer is enough to hold the data.
// A uncomressed chunk can not be bigger than the chunk size specified
// during compression (4096)
//
if ((NoOfChunks * 4096) > UnCompressedBufLen) {
// printf("Allocating UnCompressedBuf 0x%x\n", NoOfChunks * 4096);
UnCompressedBuf = FrsAlloc(NoOfChunks * 4096);
UnCompressedBufLen = NoOfChunks * 4096;
}
//
// Rewind the file pointer so we can read the remaining chunck at the next read.
//
LenOfPartialChunk = ((LONG)RestoreBufSize - (LONG)ToRestore);
LenOfPartialChunk = SetFilePointer(StageHandle, LenOfPartialChunk, NULL, FILE_CURRENT);
if (LenOfPartialChunk == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
// FrsErrorCodeMsg1(FRS_ERROR_SET_FILE_POINTER, GetLastError(), StagePath);
goto CLEANUP;;
}
// printf("Passing : UnCompressedBufLen = 0x%x, RestoreBufSize = 0x%x, NoOfChunks = %d\n",
// UnCompressedBufLen, RestoreBufSize, NoOfChunks);
NtStatus = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, // compression engine
UnCompressedBuf, // input
UnCompressedBufLen, // length of input
RestoreBuf, // output
RestoreBufSize, //
&ActUnCompressedSize); // result size
WStatus = FrsSetLastNTError(NtStatus);
if (!WIN_SUCCESS(WStatus)) {
printf("Error decompressing at file offset 0x%08x. WStatus = %d. NtStatus = 0x%08x. ActUnCompressedSize = 0x%x\n", LenOfPartialChunk, WStatus, NtStatus, ActUnCompressedSize);
goto CLEANUP;
}
// printf("Decompressed buf size = 0x%x\n", ActUnCompressedSize);
//
// restore temporary
//
// if (!BackupWrite(DstHandle, RestoreBuf, ToRestore, &Restored, FALSE, TRUE, &RestoreContext)) {
if (!BackupWrite(DstHandle, UnCompressedBuf, ActUnCompressedSize, &Restored, FALSE, TRUE, &RestoreContext)) {
WStatus = GetLastError();
if (IsDir && WIN_ALREADY_EXISTS(WStatus)) {
printf("++ ERROR - IGNORED for %ws; Directories and Alternate Data Streams!\n",DestFile);
}
//
// Uknown stream header or couldn't apply object id
//
if (WStatus == ERROR_INVALID_DATA ||
WStatus == ERROR_DUP_NAME ||
(IsDir && WIN_ALREADY_EXISTS(WStatus))) {
//
// Seek to the next stream. Stop if there are none.
//
BackupSeek(DstHandle, -1, -1, &Low, &High, &RestoreContext);
if (Low == 0 && High == 0) {
break;
}
} else {
//
// Unknown error; abort
//
goto CLEANUP;
}
}
} while (TRUE);
//
// Set times
//
WStatus = FrsSetFileTime(DestFile,
DstHandle,
(PFILETIME)&Header->Attributes.CreationTime.QuadPart,
(PFILETIME)&Header->Attributes.LastAccessTime.QuadPart,
(PFILETIME)&Header->Attributes.LastWriteTime.QuadPart);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
//
// Set final attributes (which could make the file Read Only)
// Clear the offline attrbute flag since we just wrote the file.
//
ClearFlag(Header->Attributes.FileAttributes, FILE_ATTRIBUTE_OFFLINE);
WStatus = FrsSetFileAttributes(DestFile,
DstHandle,
Header->Attributes.FileAttributes);
if (!WIN_SUCCESS(WStatus)) {
goto CLEANUP;
}
//
// Make sure all of the data is on disk. We don't want to lose
// it across reboots
//
if (!FlushFileBuffers(DstHandle)) {
goto CLEANUP;
}
//
// Return success
//
WStatus = ERROR_SUCCESS;
CLEANUP:
//
// Release resources in optimal order
//
// Leave the file lying around for a retry operation. We don't want
// to assign a new fid by deleting and recreating the file -- that
// would confuse the IDTable.
//
//
// Free up the restore context before we close TmpHandle (just in case)
//
if (RestoreContext) {
BackupWrite(DstHandle, NULL, 0, NULL, TRUE, TRUE, &RestoreContext);
}
//
// Close the Dst handle
//
if (HANDLE_IS_VALID(DstHandle)) {
//
// Truncate a partial install
//
if (!WIN_SUCCESS(WStatus)) {
if (!IsDir) {
SizeHigh = 0;
SizeLow = 0;
SizeLow = SetFilePointer(DstHandle, SizeLow, &SizeHigh, FILE_BEGIN);
if (SizeLow == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
} else if (!SetEndOfFile(DstHandle)) {
}
}
}
FRS_CLOSE(DstHandle);
}
FRS_CLOSE(StageHandle);
//
// Free the buffers in descending order by size
//
FrsFree(RestoreBuf);
FrsFree(StagePath);
//
// DONE
//
return WStatus;
}
DWORD
FrsCompressFile(
PWCHAR SrcFile,
PWCHAR DestFile
)
/*++
Routine Description:
Compresses the file.
Arguments:
SrcFile - Source file.
DestFile - Destination file.
Return Value:
WStatus.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
WStatus = StuNewGenerateStage(SrcFile, DestFile);
if (!WIN_SUCCESS(WStatus)) {
return WStatus;
}
return WStatus;
}
DWORD
FrsDeCompressFile(
PWCHAR SrcFile,
PWCHAR DestFile
)
/*++
Routine Description:
DeCompresses the file.
Arguments:
SrcFile - Source file.
DestFile - Destination file.
Return Value:
WStatus.
--*/
{
DWORD WStatus = ERROR_SUCCESS;
WStatus = StuNewExecuteInstall(SrcFile, DestFile);
if (!WIN_SUCCESS(WStatus)) {
return WStatus;
}
return WStatus;
}
VOID
Usage(
PWCHAR *Argv
)
/*++
Routine Description:
Usage messages.
Arguments:
None.
Return Value:
None.
--*/
{
printf("This tool is used to the compress and decompress files.\n\n");
printf(" /? : This help screen is displayed.\n");
printf(" /c SourceFile DestinationFile : Compress Source File and write to Destination File.\n");
printf(" /d SourceFile DestinationFile : DeCompress Source File and write to Destination File.\n");
fflush(stdout);
}
int
__cdecl main (int argc, char *argv[])
{
PWCHAR *Argv = NULL;
WCHAR SrcFile[MAX_PATH];
WCHAR DestFile[MAX_PATH];
DWORD OptLen = 0;
BOOL bCompress = FALSE;
BOOL bDeCompress = FALSE;
int i;
DWORD WStatus = ERROR_SUCCESS;
if (argc < 2 ) {
Usage(Argv);
return 0;
}
Argv = MainConvertArgV(argc,argv);
for (i = 1; i < argc; ++i) {
OptLen = wcslen(Argv[i]);
if (OptLen == 2 &&
((wcsstr(Argv[i], L"/?") == Argv[i]) ||
(wcsstr(Argv[i], L"-?") == Argv[i]))) {
Usage(Argv);
return 0;
} else if (OptLen == 2 &&
((wcsstr(Argv[i], L"/c") == Argv[i]) ||
(wcsstr(Argv[i], L"-c") == Argv[i]))) {
if (i + 2 >= argc) {
Usage(Argv);
return 0;
}
bCompress = TRUE;
wcscpy(SrcFile, Argv[i+1]);
wcscpy(DestFile, Argv[i+2]);
i+=2;
} else if (OptLen == 2 &&
((wcsstr(Argv[i], L"/d") == Argv[i]) ||
(wcsstr(Argv[i], L"-d") == Argv[i]))) {
if (i + 2 >= argc) {
Usage(Argv);
return 0;
}
bDeCompress = TRUE;
wcscpy(SrcFile, Argv[i+1]);
wcscpy(DestFile, Argv[i+2]);
i+=2;
} else {
Usage(Argv);
return 0;
}
}
DebugInfo.Disabled = TRUE;
if ((bCompress & bDeCompress) || (!bCompress & !bDeCompress)) {
Usage(Argv);
return 0;
}
if (bCompress) {
WStatus = FrsCompressFile(SrcFile, DestFile);
if (WStatus != ERROR_SUCCESS) {
printf("Error compressing file %ws. WStatus = %d\n",SrcFile, WStatus);
return 1;
}
} else if (bDeCompress) {
WStatus = FrsDeCompressFile(SrcFile, DestFile);
if (WStatus != ERROR_SUCCESS) {
printf("Error decompressing file %ws. WStatus = %d\n",SrcFile, WStatus);
return 1;
}
}
return 0;
}