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

  1. #include <ntreppch.h>
  2. #pragma hdrstop
  3. #include <frs.h>
  4. #include <tablefcn.h>
  5. #include <perrepsr.h>
  6. #define INITGUID
  7. #include "frstrace.h"
  8. /////////////////////////////////FROM MAIN.c ////////////////////////////////////
  9. PCHAR LatestChanges[] = {
  10. "Latest changes:",
  11. " RC3-Q1: 432553, 436070, 432549",
  12. " WMI Perf Tracing",
  13. " Allow all junction points",
  14. " Automatic trigger of non-auth restore on WRAP_ERROR",
  15. " Allow changing replica root path",
  16. " 03/18/00 - sudarc - checkin",
  17. " 03/15/00 - 32/64 Comm fix.",
  18. " 03/20 - merge with sudarc.",
  19. " 03/30/00 - sudarc - checkin - volatile connection cleanup.",
  20. " 04/14/00 - sudarc - checkin - bugbug, memleak, and poll summary eventlog.",
  21. NULL
  22. };
  23. HANDLE ShutDownEvent;
  24. HANDLE ShutDownComplete;
  25. HANDLE DataBaseEvent;
  26. HANDLE JournalEvent;
  27. HANDLE ChgOrdEvent;
  28. HANDLE ReplicaEvent;
  29. HANDLE CommEvent;
  30. HANDLE DsPollEvent;
  31. HANDLE DsShutDownComplete;
  32. PWCHAR ServerPrincName;
  33. BOOL IsAMember = FALSE;
  34. BOOL IsADc = FALSE;
  35. BOOL IsAPrimaryDc = FALSE;
  36. BOOL EventLogIsRunning = FALSE;
  37. BOOL RpcssIsRunning = FALSE;
  38. BOOL RunningAsAService = TRUE;
  39. BOOL NoDs = FALSE;
  40. BOOL FrsIsShuttingDown = FALSE;
  41. BOOL FrsIsAsserting = FALSE;
  42. //
  43. // Require mutual authentication
  44. //
  45. BOOL MutualAuthenticationIsEnabled;
  46. BOOL MutualAuthenticationIsEnabledAndRequired;
  47. //
  48. // Directory and file filter lists from registry.
  49. //
  50. PWCHAR RegistryFileExclFilterList;
  51. PWCHAR RegistryFileInclFilterList;
  52. PWCHAR RegistryDirExclFilterList;
  53. PWCHAR RegistryDirInclFilterList;
  54. //
  55. // Synchronize the shutdown thread with the service controller
  56. //
  57. CRITICAL_SECTION ServiceLock;
  58. //
  59. // Synchronize initialization
  60. //
  61. CRITICAL_SECTION MainInitLock;
  62. //
  63. // Convert the ANSI ArgV into a UNICODE ArgV
  64. //
  65. PWCHAR *WideArgV;
  66. //
  67. // Process Handle
  68. //
  69. HANDLE ProcessHandle;
  70. //
  71. // Working path / DB Log path
  72. //
  73. PWCHAR WorkingPath;
  74. PWCHAR DbLogPath;
  75. //
  76. // Database directories (UNICODE and ASCII)
  77. //
  78. PWCHAR JetPath;
  79. PWCHAR JetFile;
  80. PWCHAR JetFileCompact;
  81. PWCHAR JetSys;
  82. PWCHAR JetTemp;
  83. PWCHAR JetLog;
  84. PCHAR JetPathA;
  85. PCHAR JetFileA;
  86. PCHAR JetFileCompactA;
  87. PCHAR JetSysA;
  88. PCHAR JetTempA;
  89. PCHAR JetLogA;
  90. //
  91. // Limit the amount of staging area used (in kilobytes). This is
  92. // a soft limit, the actual usage may be higher.
  93. //
  94. DWORD StagingLimitInKb;
  95. //
  96. // Default staging limit in kb to be assigned to a new staging area.
  97. //
  98. DWORD DefaultStagingLimitInKb;
  99. //
  100. // Max number replica sets and Jet Sessions allowed.
  101. //
  102. ULONG MaxNumberReplicaSets;
  103. ULONG MaxNumberJetSessions;
  104. //
  105. // Maximum number of outbound changeorders allowed outstanding per connection.
  106. //
  107. ULONG MaxOutLogCoQuota;
  108. //
  109. // If TRUE then try to preserve existing file OIDs whenever possible.
  110. // -- See Bug 352250 for why this is a risky thing to do.
  111. //
  112. BOOL PreserveFileOID;
  113. //
  114. // Limits on how many time and for how long we will continue to retry a
  115. // change order when the parent is missing.
  116. //
  117. ULONG MaxCoRetryTimeoutMinutes;
  118. ULONG MaxCoRetryTimeoutCount;
  119. //
  120. // Major/minor (see frs.h)
  121. //
  122. ULONG NtFrsMajor = NTFRS_MAJOR;
  123. ULONG NtFrsMinor = NTFRS_MINOR;
  124. ULONG NtFrsStageMajor = NTFRS_STAGE_MAJOR;
  125. ULONG NtFrsStageMinor = NTFRS_STAGE_MINOR_1;
  126. ULONG NtFrsCommMinor = NTFRS_COMM_MINOR_3;
  127. PCHAR NtFrsModule = __FILE__;
  128. PCHAR NtFrsDate = __DATE__;
  129. PCHAR NtFrsTime = __TIME__;
  130. //
  131. // Shutdown timeout
  132. //
  133. ULONG ShutDownTimeOut = DEFAULT_SHUTDOWN_TIMEOUT;
  134. //
  135. // A useful thing to have around
  136. //
  137. WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 2];
  138. PWCHAR ComputerDnsName;
  139. PWCHAR ServiceLongName;
  140. //
  141. // The rpc server may reference this table as soon as the rpc interface
  142. // is registered. Make sure it is setup.
  143. //
  144. extern PGEN_TABLE ReplicasByGuid;
  145. //
  146. // The staging area table is references early in the startup process
  147. //
  148. extern PGEN_TABLE StagingAreaTable;
  149. PGEN_TABLE CompressionTable;
  150. //
  151. // Size of buffer to use when enumerating directories. Actual memory
  152. // usage will be #levels * SizeOfBuffer.
  153. //
  154. LONG EnumerateDirectorySizeInBytes;
  155. BOOL MainInitHasRun;
  156. //
  157. // Do not accept stop control unless the service is in SERVICE_RUNNING state.
  158. // This prevents the service from getting confused when a stop is called
  159. // while the service is starting.
  160. //
  161. SERVICE_STATUS ServiceStatus = {
  162. SERVICE_WIN32_OWN_PROCESS,
  163. SERVICE_START_PENDING,
  164. // SERVICE_ACCEPT_STOP |
  165. // SERVICE_ACCEPT_PAUSE_CONTINUE |
  166. SERVICE_ACCEPT_SHUTDOWN,
  167. 0,
  168. 0,
  169. 0,
  170. 60*1000
  171. };
  172. //
  173. // Supported compression formats.
  174. //
  175. //
  176. // This is the compression format for uncompressed data.
  177. //
  178. DEFINE_GUID ( /* 00000000-0000-0000-0000-000000000000 */
  179. FrsGuidCompressionFormatNone,
  180. 0x00000000,
  181. 0x0000,
  182. 0x0000,
  183. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  184. );
  185. //
  186. // This is the compression format for data compressed using NTFS's LZNT1 compression
  187. // routines.
  188. //
  189. DEFINE_GUID ( /* 64d2f7d2-2695-436d-8830-8d3c58701e15 */
  190. FrsGuidCompressionFormatLZNT1,
  191. 0x64d2f7d2,
  192. 0x2695,
  193. 0x436d,
  194. 0x88, 0x30, 0x8d, 0x3c, 0x58, 0x70, 0x1e, 0x15
  195. );
  196. //
  197. // Fixed guid for the dummy cxtion (aka GhostCxtion) assigned to orphan remote
  198. // change orders whose inbound cxtion has been deleted from the DS but they
  199. // have already past the fetching state and can finish without the real cxtion
  200. // coming back. No authentication checks are made for this dummy cxtion.
  201. //
  202. DEFINE_GUID ( /* b9d307a7-a140-4405-991e-281033f03309 */
  203. FrsGuidGhostCxtion,
  204. 0xb9d307a7,
  205. 0xa140,
  206. 0x4405,
  207. 0x99, 0x1e, 0x28, 0x10, 0x33, 0xf0, 0x33, 0x09
  208. );
  209. DEFINE_GUID ( /* 3fe2820f-3045-4932-97fe-00d10b746dbf */
  210. FrsGhostJoinGuid,
  211. 0x3fe2820f,
  212. 0x3045,
  213. 0x4932,
  214. 0x97, 0xfe, 0x00, 0xd1, 0x0b, 0x74, 0x6d, 0xbf
  215. );
  216. //
  217. // Static Ghost cxtion structure. This cxtion is assigned to orphan remote change
  218. // orders in the inbound log whose cxtion is deleted from the DS but who have already
  219. // past the fetching state and do not need the cxtion to complete processing. No
  220. // authentication checks are made for this dummy cxtion.
  221. //
  222. PCXTION FrsGhostCxtion;
  223. SERVICE_STATUS_HANDLE ServiceStatusHandle = NULL;
  224. VOID
  225. InitializeEventLog(
  226. VOID
  227. );
  228. DWORD
  229. FrsSetServiceFailureAction(
  230. VOID
  231. );
  232. VOID
  233. FrsRpcInitializeAccessChecks(
  234. VOID
  235. );
  236. BOOL
  237. FrsSetupPrivileges (
  238. VOID
  239. );
  240. VOID
  241. CfgRegAdjustTuningDefaults(
  242. VOID
  243. );
  244. VOID
  245. CommInitializeCommSubsystem(
  246. VOID
  247. );
  248. VOID
  249. SndCsInitialize(
  250. VOID
  251. );
  252. // FRS Capacity Planning
  253. //
  254. #define RESOURCE_NAME L"MofResourceName"
  255. #define IMAGE_PATH L"ntfrs.exe"
  256. DWORD FrsWmiEventTraceFlag = FALSE;
  257. TRACEHANDLE FrsWmiTraceRegistrationHandle = (TRACEHANDLE) 0;
  258. TRACEHANDLE FrsWmiTraceLoggerHandle = (TRACEHANDLE) 0;
  259. // This is the FRS control Guid for the group of Guids traced below
  260. //
  261. DEFINE_GUID ( /* 78a8f0b1-290e-4c4c-9720-c7f1ef68ce21 */
  262. FrsControlGuid,
  263. 0x78a8f0b1,
  264. 0x290e,
  265. 0x4c4c,
  266. 0x97, 0x20, 0xc7, 0xf1, 0xef, 0x68, 0xce, 0x21
  267. );
  268. // Traceable Guids start here
  269. //
  270. DEFINE_GUID ( /* 2eee6bbf-6665-44cf-8ed7-ceea1d306085 */
  271. FrsTransGuid,
  272. 0x2eee6bbf,
  273. 0x6665,
  274. 0x44cf,
  275. 0x8e, 0xd7, 0xce, 0xea, 0x1d, 0x30, 0x60, 0x85
  276. );
  277. TRACE_GUID_REGISTRATION FrsTraceGuids[] =
  278. {
  279. { & FrsTransGuid, NULL }
  280. };
  281. #define FrsGuidCount (sizeof(FrsTraceGuids) / sizeof(TRACE_GUID_REGISTRATION))
  282. //
  283. // Trace initialization / shutdown routines
  284. //
  285. VOID
  286. MainInit(
  287. VOID
  288. )
  289. /*++
  290. Routine Description:
  291. Initialize anything necessary to run the service
  292. Arguments:
  293. None.
  294. Return Value:
  295. None.
  296. --*/
  297. {
  298. #undef DEBSUB
  299. #define DEBSUB "MainInit:"
  300. EnterCriticalSection(&MainInitLock);
  301. //
  302. // No need to initialize twice
  303. //
  304. if (MainInitHasRun) {
  305. LeaveCriticalSection(&MainInitLock);
  306. return;
  307. }
  308. //
  309. // SETUP THE INFRASTRUCTURE
  310. //
  311. // DEBUG_INIT();
  312. //
  313. // Fetch the staging file limit
  314. //
  315. CfgRegReadDWord(FKC_STAGING_LIMIT, NULL, 0, &StagingLimitInKb);
  316. DPRINT1(4, ":S: Staging limit is: %d KB\n", StagingLimitInKb);
  317. //
  318. // Put the default value in registry if there is no key present.
  319. //
  320. CfgRegWriteDWord(FKC_STAGING_LIMIT,
  321. NULL,
  322. FRS_RKF_FORCE_DEFAULT_VALUE | FRS_RKF_KEEP_EXISTING_VALUE,
  323. 0);
  324. //
  325. // Get Max number of replica sets allowed.
  326. //
  327. CfgRegReadDWord(FKC_MAX_NUMBER_REPLICA_SETS, NULL, 0, &MaxNumberReplicaSets);
  328. //
  329. // Get outstanding CO qutoa limit on outbound connections.
  330. //
  331. CfgRegReadDWord(FKC_OUT_LOG_CO_QUOTA, NULL, 0, &MaxOutLogCoQuota);
  332. //
  333. // Get boolean to tell us to preserve file object IDs
  334. // -- See Bug 352250 for why this is a risky thing to do.
  335. CfgRegReadDWord(FKC_PRESERVE_FILE_OID, NULL, 0, &PreserveFileOID);
  336. //
  337. // Get the service long name for use in error messages.
  338. //
  339. ServiceLongName = FrsGetResourceStr(IDS_SERVICE_LONG_NAME);
  340. //
  341. // Initialize the Delayed command server. This command server
  342. // is really a timeout queue that the other command servers use
  343. // to retry or check the state of previously issued commands that
  344. // have an indeterminate completion time.
  345. //
  346. // WARN: MUST BE FIRST -- Some command servers may use this
  347. // command server during their initialization.
  348. //
  349. WaitInitialize();
  350. FrsDelCsInitialize();
  351. //
  352. // SETUP THE COMM LAYER
  353. //
  354. //
  355. // Initialize the low level comm subsystem
  356. //
  357. CommInitializeCommSubsystem();
  358. //
  359. // Initialize the Send command server. The receive command server
  360. // starts when we register our RPC interface.
  361. //
  362. SndCsInitialize();
  363. //
  364. // SETUP THE SUPPORT COMMAND SERVERS
  365. //
  366. //
  367. // Staging file fetcher
  368. //
  369. FrsFetchCsInitialize();
  370. //
  371. // Staging file installer
  372. //
  373. FrsInstallCsInitialize();
  374. //
  375. // Staging file generator
  376. //
  377. FrsStageCsInitialize();
  378. //
  379. // outbound log processor
  380. //
  381. OutLogInitialize();
  382. //
  383. // LAST, KICK OFF REPLICATION
  384. //
  385. //
  386. // MUST PRECEED DATABASE AND DS INITIALIZATION
  387. //
  388. // The DS command server and the Database command server depend on
  389. // the replica control initialization.
  390. //
  391. // Initialize the replica control command server
  392. //
  393. RcsInitializeReplicaCmdServer();
  394. //
  395. // Actually, we can start the database at any time after the delayed
  396. // command server and the replica control command server. But its
  397. // a good idea to fail sooner than later to make cleanup more predictable.
  398. //
  399. DbsInitialize();
  400. //
  401. // Free up memory by reducing our working set size
  402. //
  403. SetProcessWorkingSetSize(ProcessHandle, (SIZE_T)-1, (SIZE_T)-1);
  404. MainInitHasRun = TRUE;
  405. LeaveCriticalSection(&MainInitLock);
  406. }
  407. ////////////////////////////////////FROM MAIN.C////////////////////////////////////
  408. #define FREE(_p) \
  409. if (_p) free(_p);
  410. PWCHAR *
  411. MainConvertArgV(
  412. DWORD ArgC,
  413. PCHAR *ArgV
  414. )
  415. /*++
  416. Routine Description:
  417. Convert short char ArgV into wide char ArgV
  418. Arguments:
  419. ArgC - From main
  420. ArgV - From main
  421. Return Value:
  422. Address of the new ArgV
  423. --*/
  424. {
  425. #undef DEBSUB
  426. #define DEBSUB "MainConvertArgV:"
  427. PWCHAR *wideArgV;
  428. wideArgV = (PWCHAR*)malloc((ArgC + 1) * sizeof(PWCHAR));
  429. wideArgV[ArgC] = NULL;
  430. while (ArgC-- >= 1) {
  431. wideArgV[ArgC] = (PWCHAR)malloc((strlen(ArgV[ArgC]) + 1) * sizeof(WCHAR));
  432. wsprintf(wideArgV[ArgC], L"%hs", ArgV[ArgC]);
  433. if (wideArgV[ArgC]) {
  434. _wcslwr(wideArgV[ArgC]);
  435. }
  436. }
  437. return wideArgV;
  438. }
  439. #define STAGEING_IOSIZE (64 * 1024)
  440. DWORD
  441. FrsGetReparseTag(
  442. IN HANDLE Handle,
  443. OUT ULONG *ReparseTag
  444. );
  445. BOOL CompressionEnabled = TRUE;
  446. //
  447. // Local data structures
  448. //
  449. //
  450. // The compressed chunk header is the structure that starts every
  451. // new chunk in the compressed data stream. In our definition here
  452. // we union it with a ushort to make setting and retrieving the chunk
  453. // header easier. The header stores the size of the compressed chunk,
  454. // its signature, and if the data stored in the chunk is compressed or
  455. // not.
  456. //
  457. // Compressed Chunk Size:
  458. //
  459. // The actual size of a compressed chunk ranges from 4 bytes (2 byte
  460. // header, 1 flag byte, and 1 literal byte) to 4098 bytes (2 byte
  461. // header, and 4096 bytes of uncompressed data). The size is encoded
  462. // in a 12 bit field biased by 3. A value of 1 corresponds to a chunk
  463. // size of 4, 2 => 5, ..., 4095 => 4098. A value of zero is special
  464. // because it denotes the ending chunk header.
  465. //
  466. // Chunk Signature:
  467. //
  468. // The only valid signature value is 3. This denotes a 4KB uncompressed
  469. // chunk using with the 4/12 to 12/4 sliding offset/length encoding.
  470. //
  471. // Is Chunk Compressed:
  472. //
  473. // If the data in the chunk is compressed this field is 1 otherwise
  474. // the data is uncompressed and this field is 0.
  475. //
  476. // The ending chunk header in a compressed buffer contains the a value of
  477. // zero (space permitting).
  478. //
  479. typedef union _COMPRESSED_CHUNK_HEADER {
  480. struct {
  481. USHORT CompressedChunkSizeMinus3 : 12;
  482. USHORT ChunkSignature : 3;
  483. USHORT IsChunkCompressed : 1;
  484. } Chunk;
  485. USHORT Short;
  486. } COMPRESSED_CHUNK_HEADER, *PCOMPRESSED_CHUNK_HEADER;
  487. #define FRS_MAX_CHUNKS_TOUNCOMPRESS 16
  488. DWORD
  489. StuNewGenerateStage(
  490. PWCHAR SrcFile,
  491. PWCHAR DestFile
  492. )
  493. /*++
  494. Routine Description:
  495. Create and populate the staging file. Currently there are four cases
  496. of interest based on the state of Coe, FromPreExisting and Md5:
  497. Coe FromPreExisting Md5
  498. NULL FALSE NULL Fetch on demand or outlog trimmed so stage file must be regenerated
  499. NULL FALSE non-null Fetch of pre-existing file by downstream partner. check MD5.
  500. NULL TRUE NULL doesn't occur
  501. NULL TRUE non-null doesn't occur
  502. non-NULL FALSE NULL Generate stage file for local CO
  503. non-NULL FALSE non-null doesn't occur -- MD5 only generated for preexisting files
  504. non-NULL TRUE NULL doesn't occur -- MD5 always generated for preexisting files.
  505. non-NULL TRUE non-null Generate stage file from pre-existing file and send MD5 upstream to check for a match.
  506. Arguments:
  507. Coc -- ptr to change order command. NULL on incoming fetch requests from downstream partners.
  508. Coe -- ptr to change order entry. NULL when regenerating the staging file for fetch
  509. FromPreExisting -- TRUE if this staging file is being generated from a
  510. preexisting file.
  511. Md5 -- Generate the MD5 digest for the caller and return it if Non-NULL
  512. SizeOfFileGenerated - Valid when the size generated is needed, otherwise NULL
  513. Return Value:
  514. WIN32 STATUS
  515. --*/
  516. {
  517. #undef DEBSUB
  518. #define DEBSUB "StuNewGenerateStage:"
  519. OVERLAPPED OpLockOverLap;
  520. LONGLONG StreamBytesLeft;
  521. LONG BuffBytesLeft;
  522. DWORD WStatus;
  523. DWORD NumBackupDataBytes;
  524. ULONG ReparseTag;
  525. ULONG OpenOptions;
  526. WORD OldSecurityControl;
  527. WORD NewSecurityControl;
  528. WORD *SecurityControl;
  529. BOOL FirstBuffer = TRUE;
  530. BOOL Regenerating = FALSE;
  531. BOOL SkipCo = FALSE;
  532. BOOL FirstOpen = TRUE;
  533. BOOL StartOfStream = TRUE;
  534. PWCHAR StagePath = NULL;
  535. PWCHAR FinalPath = NULL;
  536. PUCHAR BackupBuf = NULL;
  537. PVOID BackupContext = NULL;
  538. HANDLE OpLockEvent = NULL;
  539. HANDLE SrcHandle = INVALID_HANDLE_VALUE;
  540. HANDLE StageHandle = INVALID_HANDLE_VALUE;
  541. HANDLE OpLockHandle = INVALID_HANDLE_VALUE;
  542. WIN32_STREAM_ID *StreamId;
  543. PSTAGE_HEADER Header = NULL;
  544. STAGE_HEADER StageHeaderMemory;
  545. ULONG Length;
  546. PREPLICA NewReplica = NULL;
  547. WCHAR TStr[100];
  548. DWORD NtStatus;
  549. PUCHAR CompressedBuf = NULL;
  550. DWORD CompressedSize;
  551. PVOID WorkSpace = NULL;
  552. DWORD WorkSpaceSize = 0;
  553. DWORD FragmentWorkSpaceSize = 0;
  554. DWORD UnCompressedFileSize = 0;
  555. DWORD CompressedFileSize = 0;
  556. OpenOptions = OPEN_OPTIONS;
  557. //
  558. // The header is located at the beginning of the newly created staging file
  559. //
  560. // Fill in the header with info from the src file
  561. // Compression type
  562. // Change order
  563. // Attributes
  564. //
  565. Header = &StageHeaderMemory;
  566. ZeroMemory(Header, sizeof(STAGE_HEADER));
  567. Header->Attributes.FileAttributes = GetFileAttributes(SrcFile);
  568. RETRY_OPEN:
  569. WStatus = FrsOpenSourceFileW(&SrcHandle,
  570. SrcFile,
  571. READ_ACCESS,
  572. OpenOptions);
  573. if (!WIN_SUCCESS(WStatus)) {
  574. goto out;
  575. }
  576. //
  577. // What type of reparse is it?
  578. //
  579. if (FirstOpen &&
  580. (Header->Attributes.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
  581. FirstOpen = FALSE;
  582. //
  583. // reparse tag
  584. //
  585. WStatus = FrsGetReparseTag(SrcHandle, &ReparseTag);
  586. if (!WIN_SUCCESS(WStatus)) {
  587. goto out;
  588. }
  589. //
  590. // We only accept operations on files with SIS and HSM reparse points.
  591. // For example a rename of a SIS file into a replica tree needs to prop
  592. // a create CO.
  593. //
  594. if ((ReparseTag != IO_REPARSE_TAG_HSM) &&
  595. (ReparseTag != IO_REPARSE_TAG_SIS)) {
  596. WIN_SET_FAIL(WStatus);
  597. goto out;
  598. }
  599. //
  600. // We hit a file with a known reparse tag type.
  601. // Close and reopen the file without the FILE_OPEN_REPARSE_POINT
  602. // option so backup read will get the underlying data.
  603. //
  604. FRS_CLOSE(SrcHandle);
  605. ClearFlag(OpenOptions, FILE_OPEN_REPARSE_POINT);
  606. goto RETRY_OPEN;
  607. }
  608. //
  609. // Assume retriable errors for the silly boolean functions
  610. //
  611. WIN_SET_RETRY(WStatus);
  612. //
  613. // Default to no compression if we can't get the compression state
  614. //
  615. if (!FrsGetCompression(SrcFile, SrcHandle, &Header->Compression)) {
  616. Header->Compression = COMPRESSION_FORMAT_NONE;
  617. }
  618. //
  619. // The backup data begins at the first 32 byte boundary following the header
  620. //
  621. Header->DataLow = QuadQuadAlignSize(sizeof(STAGE_HEADER));
  622. //
  623. // Major/minor
  624. //
  625. Header->Major = NtFrsStageMajor;
  626. Header->Minor = NtFrsStageMinor;
  627. //
  628. // Create the local staging name
  629. //
  630. StagePath = FrsWcsDup(DestFile);
  631. //
  632. // Create the staging file
  633. //
  634. WStatus = StuCreateFile(StagePath,&StageHandle);
  635. if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(StageHandle)) {
  636. goto out;
  637. }
  638. //
  639. // Approximate size of the staging file
  640. //
  641. WStatus = FrsSetFilePointer(StagePath, StageHandle,
  642. Header->Attributes.EndOfFile.HighPart,
  643. Header->Attributes.EndOfFile.LowPart);
  644. if(!WIN_SUCCESS(WStatus)) {
  645. goto out;
  646. }
  647. WStatus = FrsSetEndOfFile(StagePath, StageHandle);
  648. if(!WIN_SUCCESS(WStatus)) {
  649. goto out;
  650. }
  651. //
  652. // Rewind the file, write the header, and set the file pointer
  653. // to the next 32 byte boundary
  654. //
  655. WStatus = FrsSetFilePointer(StagePath, StageHandle, 0, 0);
  656. if(!WIN_SUCCESS(WStatus)) {
  657. goto out;
  658. }
  659. WStatus = StuWriteFile(StagePath, StageHandle, Header, sizeof(STAGE_HEADER));
  660. if(!WIN_SUCCESS(WStatus)) {
  661. goto out;
  662. }
  663. WStatus = FrsSetFilePointer(StagePath, StageHandle, 0, Header->DataLow);
  664. if(!WIN_SUCCESS(WStatus)) {
  665. goto out;
  666. }
  667. UnCompressedFileSize = Header->DataLow;
  668. CompressedFileSize = Header->DataLow;
  669. //
  670. // Backup the src file into the staging file
  671. //
  672. BackupBuf = FrsAlloc(STAGEING_IOSIZE);
  673. CompressedBuf = FrsAlloc(STAGEING_IOSIZE * 2);
  674. StreamBytesLeft = 0;
  675. NtStatus = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1,
  676. &WorkSpaceSize,
  677. &FragmentWorkSpaceSize);
  678. WStatus = FrsSetLastNTError(NtStatus);
  679. if (!WIN_SUCCESS(WStatus)) {
  680. goto out;
  681. }
  682. // printf("WorkSpaceSize = %d, FragmentWorkSpaceSize = %d\n", WorkSpaceSize, FragmentWorkSpaceSize);
  683. WorkSpace = FrsAlloc(WorkSpaceSize);
  684. while (TRUE) {
  685. //
  686. // read source
  687. //
  688. if (!BackupRead(SrcHandle,
  689. BackupBuf,
  690. STAGEING_IOSIZE,
  691. &NumBackupDataBytes,
  692. FALSE,
  693. TRUE,
  694. &BackupContext)) {
  695. goto out;
  696. }
  697. //
  698. // No more data; Backup done
  699. //
  700. if (NumBackupDataBytes == 0) {
  701. break;
  702. }
  703. UnCompressedFileSize += NumBackupDataBytes;
  704. //
  705. // write the staging file
  706. //
  707. NtStatus = RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, // compression engine
  708. BackupBuf, // input
  709. NumBackupDataBytes, // length of input
  710. CompressedBuf, // output
  711. STAGEING_IOSIZE * 2, // length of output
  712. 4096, // chunking that occurs in buffer
  713. &CompressedSize, // result size
  714. WorkSpace); // I have no clue
  715. //
  716. // STATUS_BUFFER_ALL_ZEROS means the compression worked without a hitch
  717. // and in addition the input buffer was all zeros.
  718. //
  719. if (NtStatus == STATUS_BUFFER_ALL_ZEROS) {
  720. NtStatus = STATUS_SUCCESS;
  721. }
  722. WStatus = FrsSetLastNTError(NtStatus);
  723. // printf("Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
  724. if (!WIN_SUCCESS(WStatus)) {
  725. printf("Error : Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
  726. goto out;
  727. }
  728. // printf("Original Size = %d :: Compressed Size = %d\n", NumBackupDataBytes, CompressedSize);
  729. CompressedFileSize += CompressedSize;
  730. WStatus = StuWriteFile(StagePath, StageHandle, CompressedBuf, CompressedSize);
  731. if (!WIN_SUCCESS(WStatus)) {
  732. // if (!StuWriteFile(StagePath, StageHandle, BackupBuf, NumBackupDataBytes)) {
  733. goto out;
  734. }
  735. }
  736. //
  737. // Release handles as soon as possible
  738. //
  739. FRS_CLOSE(SrcHandle);
  740. //
  741. // Make sure all of the data is on disk. We don't want to lose
  742. // it across reboots
  743. //
  744. WStatus = FrsFlushFile(StagePath, StageHandle);
  745. if (!WIN_SUCCESS(WStatus)) {
  746. goto out;
  747. }
  748. //
  749. // Done with the staging file handle
  750. //
  751. if (BackupContext) {
  752. BackupRead(StageHandle, NULL, 0, NULL, TRUE, TRUE, &BackupContext);
  753. }
  754. FRS_CLOSE(StageHandle);
  755. BackupContext = NULL;
  756. printf("%ws Orig= %d, Comp= %d, Percentage_Comp= %5.2f\n", SrcFile, UnCompressedFileSize, CompressedFileSize,
  757. ((UnCompressedFileSize - CompressedFileSize)/(float)UnCompressedFileSize) * 100);
  758. WStatus = ERROR_SUCCESS;
  759. out:
  760. //
  761. // Release resources
  762. //
  763. FRS_CLOSE(SrcHandle);
  764. if (BackupContext) {
  765. BackupRead(StageHandle, NULL, 0, NULL, TRUE, TRUE, &BackupContext);
  766. }
  767. FRS_CLOSE(StageHandle);
  768. FrsFree(BackupBuf);
  769. FrsFree(CompressedBuf);
  770. FrsFree(WorkSpace);
  771. FrsFree(StagePath);
  772. FrsFree(FinalPath);
  773. return WStatus;
  774. }
  775. ULONG
  776. StuNewExecuteInstall(
  777. IN PWCHAR SrcFile,
  778. IN PWCHAR DestFile
  779. )
  780. /*++
  781. Routine Description:
  782. Install a staging file by restoring it to a temporary file in the
  783. same directory as the file to be replaced and then renaming it
  784. to its final destination.
  785. Arguments:
  786. Coe
  787. Return Value:
  788. Win32 status -
  789. ERROR_SUCCESS - All installed or aborted. Don't retry.
  790. ERROR_GEN_FAILURE - Couldn't install bag it.
  791. ERROR_SHARING_VIOLATION - Couldn't open the target file. retry later.
  792. ERROR_DISK_FULL - Couldn't allocate the target file. retry later.
  793. ERROR_HANDLE_DISK_FULL - ?? retry later.
  794. --*/
  795. {
  796. #undef DEBSUB
  797. #define DEBSUB "StuNewExecuteInstall:"
  798. DWORD WStatus;
  799. DWORD BytesRead;
  800. ULONG Restored;
  801. ULONG ToRestore;
  802. ULONG High;
  803. ULONG Low;
  804. ULONG Flags;
  805. BOOL AttributeMissmatch;
  806. ULONG CreateDisposition;
  807. ULONG OpenOptions;
  808. BOOL IsDir;
  809. BOOL IsReparsePoint;
  810. ULONG SizeHigh;
  811. ULONG SizeLow;
  812. BOOL ExistingOid;
  813. PVOID RestoreContext = NULL;
  814. PWCHAR StagePath = NULL;
  815. PSTAGE_HEADER Header = NULL;
  816. HANDLE DstHandle = INVALID_HANDLE_VALUE;
  817. HANDLE StageHandle = INVALID_HANDLE_VALUE;
  818. PUCHAR RestoreBuf = NULL;
  819. FILE_OBJECTID_BUFFER FileObjID;
  820. STAGE_HEADER StageHeaderMemory;
  821. DWORD NtStatus;
  822. PUCHAR UnCompressedBuf = NULL;
  823. DWORD UnCompressedBufLen = 0;
  824. DWORD ActUnCompressedSize = 0;
  825. COMPRESSED_CHUNK_HEADER ChunkHeader;
  826. DWORD RestoreBufIndex = 0;
  827. DWORD RestoreBufSize = 0;
  828. LONG LenOfPartialChunk = 0;
  829. DWORD NoOfChunks = 0;
  830. //
  831. // PROCESS STAGING FILE
  832. //
  833. StagePath = FrsWcsDup(SrcFile);
  834. //
  835. // Open the stage file for shared, sequential reads
  836. //
  837. WStatus = StuOpenFile(StagePath, GENERIC_READ,&StageHandle);
  838. if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(StageHandle)) {
  839. goto CLEANUP;
  840. }
  841. //
  842. // Read the header
  843. //
  844. Header = &StageHeaderMemory;
  845. ZeroMemory(Header, sizeof(STAGE_HEADER));
  846. WStatus = StuReadFile(StagePath, StageHandle, Header, sizeof(STAGE_HEADER), &BytesRead);
  847. if (!WIN_SUCCESS(WStatus)) {
  848. printf("Can't read file %ws. Error %d\n", StagePath, WStatus);
  849. }
  850. //
  851. // Don't understand this header format
  852. //
  853. if (Header->Major != NtFrsStageMajor) {
  854. printf("Stage Header Major Version (%d) not supported. Current Service Version is %d\n",
  855. Header->Major, NtFrsStageMajor);
  856. goto CLEANUP;
  857. }
  858. //
  859. // Minor version NTFRS_STAGE_MINOR_1 had the change order extension in the
  860. // header.
  861. //
  862. /* if (Header->Minor >= NTFRS_STAGE_MINOR_0) {
  863. } else {
  864. //
  865. // This is an older stage file. No CO Extension in the header.
  866. //
  867. Header->ChangeOrderCommand.Extension = NULL;
  868. }
  869. */
  870. //
  871. // PROCESS TEMPORARY FILE
  872. //
  873. IsDir = Header->Attributes.FileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  874. IsReparsePoint = Header->Attributes.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT;
  875. CreateDisposition = FILE_OPEN;
  876. if (!IsDir) {
  877. //
  878. // In case this is an HSM file don't force the data to be read from
  879. // tape since the remote co is just going to overwrite all the data anyway.
  880. //
  881. // Setting CreateDisposition to FILE_OVERWRITE seems to cause a regression
  882. // Failure with an ACL Test where we set a deny all ACL and then the
  883. // open fails. This is a mystery for now so don't do it.
  884. // In addtion overwrite fails if RO attribute is set on file.
  885. //
  886. //CreateDisposition = FILE_OVERWRITE;
  887. printf("Target is a file\n");
  888. } else {
  889. printf("Target is a directory\n");
  890. }
  891. //
  892. // In case this is a SIS or HSM file open the underlying file not the
  893. // reparse point. For HSM, need to clear FILE_OPEN_NO_RECALL to write it.
  894. //
  895. OpenOptions = OPEN_OPTIONS;
  896. WStatus = StuCreateFile(DestFile,&DstHandle);
  897. if (!WIN_SUCCESS(WStatus) || !HANDLE_IS_VALID(DstHandle)) {
  898. goto CLEANUP;
  899. }
  900. //
  901. // Truncate the file if not a directory
  902. //
  903. if (!IsDir && !SetEndOfFile(DstHandle)) {
  904. printf("++ WARN - SetEndOfFile(%ws);", DestFile, GetLastError());
  905. }
  906. //
  907. // For the silly functions that don't return a win status
  908. //
  909. WIN_SET_FAIL(WStatus);
  910. //
  911. // Set compression mode
  912. //
  913. WStatus = FrsSetCompression(DestFile, DstHandle, Header->Compression);
  914. if (!WIN_SUCCESS(WStatus)) {
  915. goto CLEANUP;
  916. }
  917. //
  918. // Set attributes
  919. //
  920. WStatus = FrsSetFileAttributes(DestFile,
  921. DstHandle,
  922. Header->Attributes.FileAttributes &
  923. ~NOREPL_ATTRIBUTES);
  924. if (!WIN_SUCCESS(WStatus)) {
  925. goto CLEANUP;
  926. }
  927. //
  928. // Seek to the first byte of data in the stage file
  929. //
  930. WStatus = FrsSetFilePointer(StagePath, StageHandle,
  931. Header->DataHigh, Header->DataLow);
  932. if (!WIN_SUCCESS(WStatus)) {
  933. goto CLEANUP;
  934. }
  935. //
  936. // Restore the stage file into the temporary file
  937. //
  938. RestoreBuf = FrsAlloc(STAGEING_IOSIZE);
  939. UnCompressedBuf = 0;
  940. UnCompressedBufLen = 0;
  941. do {
  942. //
  943. // read stage
  944. //
  945. WStatus = StuReadFile(StagePath, StageHandle, RestoreBuf, STAGEING_IOSIZE, &ToRestore);
  946. if (!WIN_SUCCESS(WStatus)) {
  947. goto CLEANUP;
  948. }
  949. if (ToRestore == 0) {
  950. break;
  951. }
  952. RestoreBufIndex = 0;
  953. RestoreBufSize = 0;
  954. NoOfChunks = 0;
  955. while ((RestoreBufIndex <= ToRestore) && (NoOfChunks < FRS_MAX_CHUNKS_TOUNCOMPRESS)) {
  956. memcpy(&ChunkHeader, RestoreBuf + RestoreBufIndex,sizeof(COMPRESSED_CHUNK_HEADER));
  957. // printf("Chunck size is 0x%x\n", ChunkHeader.Chunk.CompressedChunkSizeMinus3);
  958. RestoreBufSize = RestoreBufIndex;
  959. ++NoOfChunks;
  960. RestoreBufIndex+=ChunkHeader.Chunk.CompressedChunkSizeMinus3+3;
  961. }
  962. //
  963. // Check if the uncompressed buffer is enough to hold the data.
  964. // A uncomressed chunk can not be bigger than the chunk size specified
  965. // during compression (4096)
  966. //
  967. if ((NoOfChunks * 4096) > UnCompressedBufLen) {
  968. // printf("Allocating UnCompressedBuf 0x%x\n", NoOfChunks * 4096);
  969. UnCompressedBuf = FrsAlloc(NoOfChunks * 4096);
  970. UnCompressedBufLen = NoOfChunks * 4096;
  971. }
  972. //
  973. // Rewind the file pointer so we can read the remaining chunck at the next read.
  974. //
  975. LenOfPartialChunk = ((LONG)RestoreBufSize - (LONG)ToRestore);
  976. LenOfPartialChunk = SetFilePointer(StageHandle, LenOfPartialChunk, NULL, FILE_CURRENT);
  977. if (LenOfPartialChunk == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
  978. // FrsErrorCodeMsg1(FRS_ERROR_SET_FILE_POINTER, GetLastError(), StagePath);
  979. goto CLEANUP;;
  980. }
  981. // printf("Passing : UnCompressedBufLen = 0x%x, RestoreBufSize = 0x%x, NoOfChunks = %d\n",
  982. // UnCompressedBufLen, RestoreBufSize, NoOfChunks);
  983. NtStatus = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, // compression engine
  984. UnCompressedBuf, // input
  985. UnCompressedBufLen, // length of input
  986. RestoreBuf, // output
  987. RestoreBufSize, //
  988. &ActUnCompressedSize); // result size
  989. WStatus = FrsSetLastNTError(NtStatus);
  990. if (!WIN_SUCCESS(WStatus)) {
  991. printf("Error decompressing at file offset 0x%08x. WStatus = %d. NtStatus = 0x%08x. ActUnCompressedSize = 0x%x\n", LenOfPartialChunk, WStatus, NtStatus, ActUnCompressedSize);
  992. goto CLEANUP;
  993. }
  994. // printf("Decompressed buf size = 0x%x\n", ActUnCompressedSize);
  995. //
  996. // restore temporary
  997. //
  998. // if (!BackupWrite(DstHandle, RestoreBuf, ToRestore, &Restored, FALSE, TRUE, &RestoreContext)) {
  999. if (!BackupWrite(DstHandle, UnCompressedBuf, ActUnCompressedSize, &Restored, FALSE, TRUE, &RestoreContext)) {
  1000. WStatus = GetLastError();
  1001. if (IsDir && WIN_ALREADY_EXISTS(WStatus)) {
  1002. printf("++ ERROR - IGNORED for %ws; Directories and Alternate Data Streams!\n",DestFile);
  1003. }
  1004. //
  1005. // Uknown stream header or couldn't apply object id
  1006. //
  1007. if (WStatus == ERROR_INVALID_DATA ||
  1008. WStatus == ERROR_DUP_NAME ||
  1009. (IsDir && WIN_ALREADY_EXISTS(WStatus))) {
  1010. //
  1011. // Seek to the next stream. Stop if there are none.
  1012. //
  1013. BackupSeek(DstHandle, -1, -1, &Low, &High, &RestoreContext);
  1014. if (Low == 0 && High == 0) {
  1015. break;
  1016. }
  1017. } else {
  1018. //
  1019. // Unknown error; abort
  1020. //
  1021. goto CLEANUP;
  1022. }
  1023. }
  1024. } while (TRUE);
  1025. //
  1026. // Set times
  1027. //
  1028. WStatus = FrsSetFileTime(DestFile,
  1029. DstHandle,
  1030. (PFILETIME)&Header->Attributes.CreationTime.QuadPart,
  1031. (PFILETIME)&Header->Attributes.LastAccessTime.QuadPart,
  1032. (PFILETIME)&Header->Attributes.LastWriteTime.QuadPart);
  1033. if (!WIN_SUCCESS(WStatus)) {
  1034. goto CLEANUP;
  1035. }
  1036. //
  1037. // Set final attributes (which could make the file Read Only)
  1038. // Clear the offline attrbute flag since we just wrote the file.
  1039. //
  1040. ClearFlag(Header->Attributes.FileAttributes, FILE_ATTRIBUTE_OFFLINE);
  1041. WStatus = FrsSetFileAttributes(DestFile,
  1042. DstHandle,
  1043. Header->Attributes.FileAttributes);
  1044. if (!WIN_SUCCESS(WStatus)) {
  1045. goto CLEANUP;
  1046. }
  1047. //
  1048. // Make sure all of the data is on disk. We don't want to lose
  1049. // it across reboots
  1050. //
  1051. if (!FlushFileBuffers(DstHandle)) {
  1052. goto CLEANUP;
  1053. }
  1054. //
  1055. // Return success
  1056. //
  1057. WStatus = ERROR_SUCCESS;
  1058. CLEANUP:
  1059. //
  1060. // Release resources in optimal order
  1061. //
  1062. // Leave the file lying around for a retry operation. We don't want
  1063. // to assign a new fid by deleting and recreating the file -- that
  1064. // would confuse the IDTable.
  1065. //
  1066. //
  1067. // Free up the restore context before we close TmpHandle (just in case)
  1068. //
  1069. if (RestoreContext) {
  1070. BackupWrite(DstHandle, NULL, 0, NULL, TRUE, TRUE, &RestoreContext);
  1071. }
  1072. //
  1073. // Close the Dst handle
  1074. //
  1075. if (HANDLE_IS_VALID(DstHandle)) {
  1076. //
  1077. // Truncate a partial install
  1078. //
  1079. if (!WIN_SUCCESS(WStatus)) {
  1080. if (!IsDir) {
  1081. SizeHigh = 0;
  1082. SizeLow = 0;
  1083. SizeLow = SetFilePointer(DstHandle, SizeLow, &SizeHigh, FILE_BEGIN);
  1084. if (SizeLow == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
  1085. } else if (!SetEndOfFile(DstHandle)) {
  1086. }
  1087. }
  1088. }
  1089. FRS_CLOSE(DstHandle);
  1090. }
  1091. FRS_CLOSE(StageHandle);
  1092. //
  1093. // Free the buffers in descending order by size
  1094. //
  1095. FrsFree(RestoreBuf);
  1096. FrsFree(StagePath);
  1097. //
  1098. // DONE
  1099. //
  1100. return WStatus;
  1101. }
  1102. DWORD
  1103. FrsCompressFile(
  1104. PWCHAR SrcFile,
  1105. PWCHAR DestFile
  1106. )
  1107. /*++
  1108. Routine Description:
  1109. Compresses the file.
  1110. Arguments:
  1111. SrcFile - Source file.
  1112. DestFile - Destination file.
  1113. Return Value:
  1114. WStatus.
  1115. --*/
  1116. {
  1117. DWORD WStatus = ERROR_SUCCESS;
  1118. WStatus = StuNewGenerateStage(SrcFile, DestFile);
  1119. if (!WIN_SUCCESS(WStatus)) {
  1120. return WStatus;
  1121. }
  1122. return WStatus;
  1123. }
  1124. DWORD
  1125. FrsDeCompressFile(
  1126. PWCHAR SrcFile,
  1127. PWCHAR DestFile
  1128. )
  1129. /*++
  1130. Routine Description:
  1131. DeCompresses the file.
  1132. Arguments:
  1133. SrcFile - Source file.
  1134. DestFile - Destination file.
  1135. Return Value:
  1136. WStatus.
  1137. --*/
  1138. {
  1139. DWORD WStatus = ERROR_SUCCESS;
  1140. WStatus = StuNewExecuteInstall(SrcFile, DestFile);
  1141. if (!WIN_SUCCESS(WStatus)) {
  1142. return WStatus;
  1143. }
  1144. return WStatus;
  1145. }
  1146. VOID
  1147. Usage(
  1148. PWCHAR *Argv
  1149. )
  1150. /*++
  1151. Routine Description:
  1152. Usage messages.
  1153. Arguments:
  1154. None.
  1155. Return Value:
  1156. None.
  1157. --*/
  1158. {
  1159. printf("This tool is used to the compress and decompress files.\n\n");
  1160. printf(" /? : This help screen is displayed.\n");
  1161. printf(" /c SourceFile DestinationFile : Compress Source File and write to Destination File.\n");
  1162. printf(" /d SourceFile DestinationFile : DeCompress Source File and write to Destination File.\n");
  1163. fflush(stdout);
  1164. }
  1165. int
  1166. __cdecl main (int argc, char *argv[])
  1167. {
  1168. PWCHAR *Argv = NULL;
  1169. WCHAR SrcFile[MAX_PATH];
  1170. WCHAR DestFile[MAX_PATH];
  1171. DWORD OptLen = 0;
  1172. BOOL bCompress = FALSE;
  1173. BOOL bDeCompress = FALSE;
  1174. int i;
  1175. DWORD WStatus = ERROR_SUCCESS;
  1176. if (argc < 2 ) {
  1177. Usage(Argv);
  1178. return 0;
  1179. }
  1180. Argv = MainConvertArgV(argc,argv);
  1181. for (i = 1; i < argc; ++i) {
  1182. OptLen = wcslen(Argv[i]);
  1183. if (OptLen == 2 &&
  1184. ((wcsstr(Argv[i], L"/?") == Argv[i]) ||
  1185. (wcsstr(Argv[i], L"-?") == Argv[i]))) {
  1186. Usage(Argv);
  1187. return 0;
  1188. } else if (OptLen == 2 &&
  1189. ((wcsstr(Argv[i], L"/c") == Argv[i]) ||
  1190. (wcsstr(Argv[i], L"-c") == Argv[i]))) {
  1191. if (i + 2 >= argc) {
  1192. Usage(Argv);
  1193. return 0;
  1194. }
  1195. bCompress = TRUE;
  1196. wcscpy(SrcFile, Argv[i+1]);
  1197. wcscpy(DestFile, Argv[i+2]);
  1198. i+=2;
  1199. } else if (OptLen == 2 &&
  1200. ((wcsstr(Argv[i], L"/d") == Argv[i]) ||
  1201. (wcsstr(Argv[i], L"-d") == Argv[i]))) {
  1202. if (i + 2 >= argc) {
  1203. Usage(Argv);
  1204. return 0;
  1205. }
  1206. bDeCompress = TRUE;
  1207. wcscpy(SrcFile, Argv[i+1]);
  1208. wcscpy(DestFile, Argv[i+2]);
  1209. i+=2;
  1210. } else {
  1211. Usage(Argv);
  1212. return 0;
  1213. }
  1214. }
  1215. DebugInfo.Disabled = TRUE;
  1216. if ((bCompress & bDeCompress) || (!bCompress & !bDeCompress)) {
  1217. Usage(Argv);
  1218. return 0;
  1219. }
  1220. if (bCompress) {
  1221. WStatus = FrsCompressFile(SrcFile, DestFile);
  1222. if (WStatus != ERROR_SUCCESS) {
  1223. printf("Error compressing file %ws. WStatus = %d\n",SrcFile, WStatus);
  1224. return 1;
  1225. }
  1226. } else if (bDeCompress) {
  1227. WStatus = FrsDeCompressFile(SrcFile, DestFile);
  1228. if (WStatus != ERROR_SUCCESS) {
  1229. printf("Error decompressing file %ws. WStatus = %d\n",SrcFile, WStatus);
  1230. return 1;
  1231. }
  1232. }
  1233. return 0;
  1234. }