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
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;
|
|
}
|