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.

1658 lines
64 KiB

  1. #define _MODULE_VERSION_STR "X-1.3"
  2. #define _MODULE_STR "CVssCluster"
  3. #define _AUTHOR_STR "Conor Morrison"
  4. #pragma comment (compiler)
  5. #pragma comment (exestr, _MODULE_STR _MODULE_VERSION_STR)
  6. #pragma comment (user, _MODULE_STR _MODULE_VERSION_STR " Compiled on " __DATE__ " at " __TIME__ " by " _AUTHOR_STR)
  7. //++
  8. //
  9. // Copyright (c) 2000 Microsoft Corporation
  10. //
  11. // FACILITY:
  12. //
  13. // CVssCluster
  14. //
  15. // MODULE DESCRIPTION:
  16. //
  17. // Implements cluster support for Vss (i.e. NT backup).
  18. //
  19. // ENVIRONMENT:
  20. //
  21. // User mode as part of an NT service. Adheres to the following state
  22. // transition diagram for CVssWriter:
  23. //
  24. // OnBackupComplete
  25. // Backup Complete <----------------IDLE -------------->Create Writer Metadata
  26. // | ^|^ |
  27. // +--------------------------+|+-------------------------+
  28. // |
  29. // |OnBackupPrepare
  30. // |
  31. // | OnAbort
  32. // PREPARE BACKUP ---------> to IDLE
  33. // |
  34. // |OnPrepareSnapshot
  35. // |
  36. // | OnAbort
  37. // PREPARE SNAPSHOT ---------> to IDLE
  38. // |
  39. // |OnFreeze
  40. // |
  41. // | OnAbort
  42. // FREEZE ---------> to IDLE
  43. // |
  44. // |OnThaw
  45. // |
  46. // | OnAbort
  47. // THAW ---------> to IDLE
  48. //
  49. //
  50. // AUTHOR:
  51. //
  52. // Conor Morrison
  53. //
  54. // CREATION DATE:
  55. //
  56. // 18-Apr-2001
  57. //
  58. // Revision History:
  59. //
  60. // X-1 CM Conor Morrison 18-Apr-2001
  61. // Initial version to address bug #367566.
  62. // .1 Set restore method to custom and reboot required to false.
  63. // Check for component selected or bootable system state in
  64. // OnPrepareSnapshot and ignore if not. Add cleanup to Abort and
  65. // Thaw. Fix bug in RemoveDirectoryTree.
  66. // .2 Incorporate first review comments: change caption to be the component
  67. // name. Set bRestoreMetadata to false. Remove extraneous tracing.
  68. // Release the interface in the while loop. Cleanup after a non-cleanly
  69. // terminated backup. This is done in OnPrepareSnapshot. Tolerate
  70. // error_file_not_found at various places.
  71. // .3 More review comments. Reset g_bDoBackup in the prepare routine.
  72. // SetWriterFailure in more places - any time we veto we should set this.
  73. //--
  74. extern "C" {
  75. #define QFS_DO_NOT_UNMAP_WIN32
  76. #include "service.h"
  77. //CMCM! Mask build breaks.
  78. #define _LMERRLOG_
  79. #define _LMHLOGDEFINED_
  80. #define _LMAUDIT_
  81. #include "lm.h" // for SHARE_INFO_502
  82. }
  83. #include "CVssClusterp.h"
  84. // Up the warning level to 4 - we can survive...
  85. //
  86. #pragma warning( push, 4 )
  87. //
  88. // Globals
  89. //
  90. UNICODE_STRING g_ucsBackupPathLocal, g_ucsClusdbBackupPathLocal;
  91. bool g_bDoBackup; // Assume we are not enabled until we find out otherwise.
  92. //
  93. // Forward declarations for static functions.
  94. //
  95. static HRESULT StringAllocate( PUNICODE_STRING pucsString, USHORT usMaximumStringLengthInBytes );
  96. static void StringFree( PUNICODE_STRING pucsString );
  97. static void StringAppendString( PUNICODE_STRING pucsTarget, PWCHAR pwszSource );
  98. static void StringAppendString( PUNICODE_STRING pucsTarget, PUNICODE_STRING pucsSource );
  99. static HRESULT StringTruncate (PUNICODE_STRING pucsString, USHORT usSizeInChars);
  100. static HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString, DWORD dwExtraChars);
  101. static HRESULT StringCreateFromExpandedString( PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars);
  102. static HRESULT DoClusterDatabaseBackup( void );
  103. static HRESULT ConstructSecurityAttributes( PSECURITY_ATTRIBUTES psaSecurityAttributes,
  104. BOOL bIncludeBackupOperator );
  105. static VOID CleanupSecurityAttributes( PSECURITY_ATTRIBUTES psaSecurityAttributes );
  106. static HRESULT CreateTargetDirectory( OUT UNICODE_STRING* pucsTarget, IN BOOL fBootableSystemState );
  107. static HRESULT CleanupTargetDirectory( LPCWSTR pwszTargetPath );
  108. static HRESULT RemoveDirectoryTree (PUNICODE_STRING pucsDirectoryPath);
  109. //
  110. // Some useful macros.
  111. //
  112. #define LOGERROR( _hr, _func ) ClRtlLogPrint( LOG_CRITICAL, "VSS: Error: 0x%1!08lx! from: %2\n", (_hr), L#_func )
  113. #ifdef DBG
  114. #define LOGFUNCTIONENTRY( _name ) ClRtlLogPrint( LOG_NOISE, "VSS: Function: " #_name " Called.\n" )
  115. #define LOGFUNCTIONEXIT( _name ) ClRtlLogPrint( LOG_NOISE, "VSS: Function: " #_name " Exiting.\n" )
  116. #define LOGUNICODESTRING( _ustr ) ClRtlLogPrint( LOG_NOISE, "VSS: String %1!ws! == %2!ws!\n", L#_ustr, (_ustr).Buffer );
  117. #define LOGSTRING( _str ) ClRtlLogPrint( LOG_NOISE, "VSS: String %1!ws! == %2!ws!\n", L#_str, _str );
  118. #else
  119. #define LOGFUNCTIONENTRY( _name )
  120. #define LOGFUNCTIONEXIT( _name )
  121. #define LOGUNICODESTRING( _ustr )
  122. #define LOGSTRING( _str )
  123. #endif
  124. #define GET_HR_FROM_BOOL(_bSucceed) ((_bSucceed) ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
  125. #define HandleInvalid(_Handle) ((NULL == (_Handle)) || (INVALID_HANDLE_VALUE == (_Handle)))
  126. #define GET_HR_FROM_HANDLE(_handle) ((!HandleInvalid(_handle)) ? NOERROR : HRESULT_FROM_WIN32 (GetLastError( )))
  127. #define GET_HR_FROM_POINTER(_ptr) ((NULL != (_ptr)) ? NOERROR : E_OUTOFMEMORY)
  128. #define IS_VALID_PATH( _path ) ( ( ( pwszPathName[0] == DIR_SEP_CHAR ) && ( pwszPathName[1] == DIR_SEP_CHAR ) ) || \
  129. ( isalpha( pwszPathName[0] ) && ( pwszPathName[1] == L':' ) && ( pwszPathName[2] == DIR_SEP_CHAR ) ) )
  130. #define StringZero( _pucs ) ( (_pucs)->Buffer = NULL, (_pucs)->Length = 0, (_pucs)->MaximumLength = 0 )
  131. //
  132. // Defines that identify us as a product to VSS - these are the same as in the old shim
  133. //
  134. #define COMPONENT_NAME L"Cluster Database"
  135. #define APPLICATION_STRING L"ClusterDatabase"
  136. #define SHARE_NAME L"__NtBackup_cluster"
  137. // Some borrowed defines from the shim stuff.
  138. //
  139. #ifndef DIR_SEP_STRING
  140. #define DIR_SEP_STRING L"\\"
  141. #endif
  142. #ifndef DIR_SEP_CHAR
  143. #define DIR_SEP_CHAR L'\\'
  144. #endif
  145. //
  146. // Define some constants that are borrowed from the original shim. These will
  147. // be used to build the path to the directory in which the cluster files will
  148. // be placed by the cluster backup. TARGET_PATH gives this full directory. In
  149. // Identify we tell the backup app which directory we are using so it knows
  150. // where to get the files from.
  151. //
  152. #define ROOT_REPAIR_DIR L"%SystemRoot%\\Repair"
  153. #define BACKUP_SUBDIR L"\\Backup"
  154. #define BOOTABLE_STATE_SUBDIR L"\\BootableSystemState"
  155. #define SERVICE_STATE_SUBDIR L"\\ServiceState"
  156. #define TARGET_PATH ROOT_REPAIR_DIR BACKUP_SUBDIR BOOTABLE_STATE_SUBDIR DIR_SEP_STRING APPLICATION_STRING
  157. //++
  158. // DESCRIPTION: CreateIfNotExistAndSetAttributes
  159. //
  160. // Create the directory specified by pucsTarget if it does not
  161. // already exist and give it the security attributes supplied.
  162. //
  163. // PARAMETERS:
  164. // pucsTarget - string for the directory to create. Full path, possibly
  165. // with %var%
  166. // lpSecurityAttributes - Pointer to security attributes to apply to
  167. // directories created.
  168. // dwExtraAttributes - Additional attributes to apply to the directory.
  169. //
  170. // PRE-CONDITIONS:
  171. // None
  172. //
  173. // POST-CONDITIONS:
  174. // Directory created (or it already existed).
  175. //
  176. // RETURN VALUE:
  177. // S_OK - All went OK, directory created and set with attributes and
  178. // security supplied.
  179. // Error status from creating directory or setting attributes. Note that
  180. // ALREADY_EXISTS is not returned if the directory already exists.
  181. // However, if it a FILE of the same name as pucsTarget exists then this
  182. // error can be returned.
  183. //--
  184. static HRESULT CreateIfNotExistAndSetAttributes( UNICODE_STRING* pucsTarget,
  185. IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  186. IN DWORD dwExtraAttributes)
  187. {
  188. LOGFUNCTIONENTRY( CreateIfNotExistAndSetAttributes );
  189. HRESULT hr = S_OK;
  190. // Create the directory
  191. //
  192. LOGUNICODESTRING( *pucsTarget );
  193. GET_HR_FROM_BOOL( CreateDirectoryW (pucsTarget->Buffer, lpSecurityAttributes ) );
  194. ClRtlLogPrint( LOG_NOISE, "VSS: CreateIfNotExistAndSetAttributes: CreateDirectory returned: 0x%1!08lx!\n", hr );
  195. if ( hr == HRESULT_FROM_WIN32( ERROR_ALREADY_EXISTS ) ) {
  196. DWORD dwObjAttribs = GetFileAttributesW( pucsTarget->Buffer );
  197. if (( dwObjAttribs != 0xFFFFFFFF ) && ( dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY ))
  198. hr = S_OK;
  199. }
  200. // Note that we can fail with ALREADY_EXISTS if it is a file by the check above.
  201. //
  202. if ( FAILED ( hr )) {
  203. LOGERROR( hr, CreateDirectoryW );
  204. goto ErrorExit;
  205. }
  206. // Set the extra attributes
  207. //
  208. if ( dwExtraAttributes != 0 ) {
  209. GET_HR_FROM_BOOL( SetFileAttributesW (pucsTarget->Buffer, dwExtraAttributes ));
  210. if ( FAILED ( hr )) {
  211. LOGERROR( hr, SetFileAttributesW );
  212. goto ErrorExit;
  213. }
  214. }
  215. goto ret;
  216. ErrorExit:
  217. CL_ASSERT( FAILED( hr ));
  218. ret:
  219. LOGFUNCTIONEXIT( CreateIfNotExistAndSetAttributes );
  220. return hr;
  221. }
  222. //++
  223. // DESCRIPTION: CreateTargetDirectory
  224. //
  225. // Create a new target directory (hardcoded) and return it in
  226. // pucsTarget member variable if not NULL. It will create any
  227. // necessary. Uses helper function that tolerates
  228. // ERROR_ALREADY_EXISTS.
  229. //
  230. // PARAMETERS:
  231. // pucsTarget - Address to receive unicode string giving path to
  232. // directory.
  233. //
  234. // PRE-CONDITIONS:
  235. // pucsTarget must be all zeros.
  236. //
  237. // POST-CONDITIONS:
  238. // pucsTarget points to buffer containing dir string. Memory was
  239. // allocated for this buffer.
  240. //
  241. // RETURN VALUE:
  242. // S_OK - all went well
  243. // Errors from creating directories or memory allocation failure.
  244. //--
  245. static HRESULT CreateTargetDirectory( OUT UNICODE_STRING* pucsTarget, IN BOOL fBootableSystemState )
  246. {
  247. LOGFUNCTIONENTRY( CreateTargetDirectory );
  248. HRESULT hr = NOERROR;
  249. SECURITY_ATTRIBUTES saSecurityAttributes, *psaSecurityAttributes=&saSecurityAttributes;
  250. SECURITY_DESCRIPTOR sdSecurityDescriptor;
  251. bool bSecurityAttributesConstructed = false;
  252. const DWORD dwExtraAttributes =
  253. FILE_ATTRIBUTE_ARCHIVE
  254. | FILE_ATTRIBUTE_HIDDEN
  255. | FILE_ATTRIBUTE_SYSTEM
  256. | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  257. //
  258. // We really want a no access acl on this directory but because of various
  259. // problems with the EventLog and ConfigDir writers we will settle for
  260. // admin or backup operator access only. The only possible accessor is
  261. // Backup which is supposed to have the SE_BACKUP_NAME priv which will
  262. // effectively bypass the ACL. No one else needs to see this stuff.
  263. //
  264. saSecurityAttributes.nLength = sizeof( saSecurityAttributes );
  265. saSecurityAttributes.lpSecurityDescriptor = &sdSecurityDescriptor;
  266. saSecurityAttributes.bInheritHandle = false;
  267. hr = ConstructSecurityAttributes( &saSecurityAttributes, false );
  268. if ( FAILED( hr )) {
  269. LOGERROR( hr, ConstructSecurityAttributes );
  270. goto ErrorExit;
  271. }
  272. bSecurityAttributesConstructed = true;
  273. // OK, now we have attributes we can do the directories.
  274. //
  275. // First expand the Root, checking that our input is NULL.
  276. //
  277. CL_ASSERT( pucsTarget->Buffer == NULL );
  278. hr = StringCreateFromExpandedString( pucsTarget, ROOT_REPAIR_DIR, MAX_PATH );
  279. if ( FAILED( hr )) {
  280. LOGERROR( hr, StringCreateFromExpandedString );
  281. goto ErrorExit;
  282. }
  283. hr = CreateIfNotExistAndSetAttributes( pucsTarget, psaSecurityAttributes, dwExtraAttributes );
  284. if ( FAILED ( hr )) {
  285. LOGERROR( hr, CreateIfNotExistAndSetAttributes );
  286. goto ErrorExit;
  287. }
  288. StringAppendString( pucsTarget, BACKUP_SUBDIR );
  289. hr = CreateIfNotExistAndSetAttributes( pucsTarget, psaSecurityAttributes, dwExtraAttributes );
  290. if ( FAILED ( hr )) {
  291. LOGERROR( hr, CreateIfNotExistAndSetAttributes );
  292. goto ErrorExit;
  293. }
  294. if ( fBootableSystemState ) {
  295. StringAppendString( pucsTarget, BOOTABLE_STATE_SUBDIR );
  296. } else
  297. {
  298. StringAppendString( pucsTarget, SERVICE_STATE_SUBDIR );
  299. }
  300. hr = CreateIfNotExistAndSetAttributes( pucsTarget, psaSecurityAttributes, dwExtraAttributes );
  301. if ( FAILED ( hr )) {
  302. LOGERROR( hr, CreateIfNotExistAndSetAttributes );
  303. goto ErrorExit;
  304. }
  305. StringAppendString( pucsTarget, DIR_SEP_STRING APPLICATION_STRING );
  306. hr = CreateIfNotExistAndSetAttributes( pucsTarget, psaSecurityAttributes, dwExtraAttributes );
  307. if ( FAILED ( hr )) {
  308. LOGERROR( hr, CreateIfNotExistAndSetAttributes );
  309. goto ErrorExit;
  310. }
  311. // At this point we have TARGET_PATH created.
  312. //
  313. goto ret;
  314. ErrorExit:
  315. CL_ASSERT( FAILED( hr ));
  316. (void) CleanupTargetDirectory( pucsTarget->Buffer );
  317. ret:
  318. // In all cases we don't need the security attributes any more.
  319. //
  320. if ( bSecurityAttributesConstructed )
  321. CleanupSecurityAttributes( &saSecurityAttributes );
  322. return hr ;
  323. }
  324. //
  325. // There are only some valid statuses to pass to SetWriterFailure. These are
  326. // listed below. For now we just return VSS_E_WRITEERROR_NONRETRYABLE. We
  327. // could perhaps switch on the status and return something different depending
  328. // on hr.
  329. // VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT The snapshot contains only a
  330. // subset of the volumes needed to correctly back up an application
  331. // component.
  332. // VSS_E_WRITERERROR_NONRETRYABLE The writer failed due to an error that
  333. // would likely occur if another snapshot is created.
  334. // VSS_E_WRITERERROR_OUTRESOURCES The writer failed due to a resource
  335. // allocation error.
  336. // VSS_E_WRITERERROR_RETRYABLE The writer failed due to an error that would
  337. // likely not occur if another snapshot is created.
  338. // VSS_E_WRITERERROR_TIMEOUT The writer could not complete the snapshot
  339. // creation process due to a time-out between the freeze and thaw states.
  340. #if defined DBG
  341. #define SETWRITERFAILURE( ) { \
  342. HRESULT __hrTmp = SetWriterFailure( VSS_E_WRITERERROR_NONRETRYABLE ); \
  343. if ( FAILED( __hrTmp )) ClRtlLogPrint( LOG_CRITICAL, "VSS: Error from SetWriterFailure: %1!u!\n", (__hrTmp)); \
  344. CL_ASSERT( !FAILED( __hrTmp )); \
  345. }
  346. #else
  347. #define SETWRITERFAILURE( ) { \
  348. (void) SetWriterFailure( VSS_E_WRITERERROR_NONRETRYABLE ); \
  349. }
  350. #endif
  351. #define NameIsDotOrDotDot(_ptszName) \
  352. (( L'.' == (_ptszName) [0]) \
  353. && ((L'\0' == (_ptszName) [1]) \
  354. || ((L'.' == (_ptszName) [1]) \
  355. && (L'\0' == (_ptszName) [2]))))
  356. //++
  357. // DESCRIPTION: CVssWriterCluster::OnIdentify
  358. //
  359. // Callback when a request for metadata comes in. This routine
  360. // identifies this applications special needs to the backup
  361. // utility.
  362. //
  363. // PARAMETERS:
  364. // IVssCreateWriterMetadata - Interface for some methods we can call.
  365. //
  366. // PRE-CONDITIONS:
  367. // Called from Idle state
  368. //
  369. // POST-CONDITIONS:
  370. // Backup returns to idle state.
  371. //
  372. // RETURN VALUE:
  373. // true - continue with snapshot operation.
  374. // false - Veto the snapshot creation.
  375. //--
  376. bool STDMETHODCALLTYPE CVssWriterCluster::OnIdentify(IN IVssCreateWriterMetadata *pMetadata)
  377. {
  378. LOGFUNCTIONENTRY( OnIdentify );
  379. HRESULT hr = S_OK;
  380. bool bRet = true;
  381. ClRtlLogPrint( LOG_NOISE, "VSS: OnIdentify. CVssCluster.cpp version %1!hs! Add Component %2!hs!\n",
  382. _MODULE_VERSION_STR, COMPONENT_NAME );
  383. // Add ourselves to the components.
  384. //
  385. hr = pMetadata->AddComponent (VSS_CT_FILEGROUP, // VSS_COMPONENT_TYPE enumeration value.
  386. NULL, // Pointer to a string containing the logical path of the DB or file group.
  387. COMPONENT_NAME, // Pointer to a string containing the name of the component.
  388. COMPONENT_NAME, // Pointer to a string containing the description of the component.
  389. NULL, // Pointer to a bitmap of the icon representing the database (for UI)
  390. 0, // Number of bytes in bitmap.
  391. false, // bRestoreMetadata - Boolean is true if there is writer metadata associated
  392. // with the backup of the component and false if not.
  393. false, // bNotifyOnBackupComplete
  394. false); // bSelectable - true if the component can be selectively backed up.
  395. if ( FAILED( hr )) {
  396. LOGERROR( hr, IVssCreateWriterMetadata::AddComponent );
  397. bRet = false; // veto on failure
  398. goto ErrorExit;
  399. }
  400. ClRtlLogPrint( LOG_NOISE, "VSS: OnIdentify. Add Files To File Group target path: %1!ws!\n", TARGET_PATH );
  401. hr= pMetadata->AddFilesToFileGroup (NULL,
  402. COMPONENT_NAME,
  403. TARGET_PATH,
  404. L"*",
  405. true,
  406. NULL);
  407. if ( FAILED ( hr )) {
  408. LOGERROR( hr, IVssCreateWriterMetadata::AddFilesToFileGroup );
  409. bRet = false; // veto on failure
  410. goto ErrorExit;
  411. }
  412. // If we decide to go for copying the checkpoint file to the
  413. // CLUSDB for restore then we need to setup an alternate mapping.
  414. //
  415. // IVssCreateWriterMetadata::AddAlternateLocationMapping
  416. // [This is preliminary documentation and subject to change.]
  417. //
  418. // The AddAlternateLocationMapping method creates an alternate location mapping.
  419. //
  420. // HRESULT AddAlternateLocationMapping(
  421. // LPCWSTR wszPath,
  422. // LPCWSTR wszFilespec,
  423. // bool bRecursive,
  424. // LPCWSTR wszDestination
  425. // );
  426. // Now, set the restore method to custom. This is because we need
  427. // special actions for restore.
  428. //
  429. hr = pMetadata->SetRestoreMethod( VSS_RME_CUSTOM, // VSS_RESTOREMETHOD_ENUM Method,
  430. L"", // LPCWSTR wszService,
  431. NULL, // LPCWSTR wszUserProcedure,
  432. VSS_WRE_NEVER, // VSS_WRITERRESTORE_ENUM wreWriterRestore,
  433. false // bool bRebootRequired
  434. );
  435. // wszUserProcedure [out] String containing the URL of an HTML or
  436. // XML document describing to the user how the restore is to be
  437. // performed.
  438. if ( FAILED ( hr )) {
  439. LOGERROR( hr, IVssCreateWriterMetadata::SetRestoreMethod );
  440. bRet = false; // veto on failure
  441. goto ErrorExit;
  442. }
  443. goto ret;
  444. ErrorExit:
  445. CL_ASSERT( FAILED( hr ));
  446. CL_ASSERT( bRet == false );
  447. SETWRITERFAILURE( );
  448. ret:
  449. return bRet;
  450. }
  451. // callback for prepare backup event
  452. //
  453. bool STDMETHODCALLTYPE CVssWriterCluster::OnPrepareBackup(IN IVssWriterComponents *pComponent)
  454. {
  455. LOGFUNCTIONENTRY( OnPrepareBackup );
  456. bool bRet = true;
  457. UINT cComponents = 0;
  458. IVssComponent* pMyComponent = NULL;
  459. BSTR pwszName;
  460. g_bDoBackup = false; // Assume false until the loop below or IsBootableSystemStateBackedUp tells us otherwise.
  461. HRESULT hr = pComponent->GetComponentCount( &cComponents );
  462. ClRtlLogPrint( LOG_NOISE, "VSS: GetComponentCount returned hr: 0x%1!08lx! cComponents: %2!u!\n", hr, cComponents );
  463. if ( FAILED( hr )) {
  464. LOGERROR( hr, GetComponentCount );
  465. bRet = false;
  466. goto ErrorExit;
  467. }
  468. // Now, loop over all the components and see if we are there.
  469. //
  470. for ( UINT iComponent = 0; !g_bDoBackup && iComponent < cComponents; ++iComponent ) {
  471. hr = pComponent->GetComponent( iComponent, &pMyComponent );
  472. if ( FAILED( hr )) {
  473. ClRtlLogPrint( LOG_CRITICAL, "VSS: Failed to get Component: %1!u! hr: 0x%2!08lx!\n", iComponent, hr );
  474. bRet = false;
  475. goto ErrorExit;
  476. }
  477. ClRtlLogPrint( LOG_CRITICAL, "VSS: Got Component: 0x%1!08lx!\n", pMyComponent );
  478. // Now check the name.
  479. //
  480. hr = pMyComponent->GetComponentName( &pwszName );
  481. if ( FAILED( hr )) {
  482. ClRtlLogPrint( LOG_CRITICAL, "VSS: Failed to get Component Name hr: 0x%1!08lx!\n", hr );
  483. bRet = false;
  484. pMyComponent->Release( );
  485. goto ErrorExit;
  486. }
  487. ClRtlLogPrint( LOG_CRITICAL, "VSS: Got component: %1!ws!\n", pwszName );
  488. if ( wcscmp ( pwszName, COMPONENT_NAME ) == 0 )
  489. g_bDoBackup = true;
  490. SysFreeString( pwszName );
  491. pMyComponent->Release( );
  492. }
  493. // OK, explicitly selected component count is 0 but we can be part
  494. // of a backup of bootable system state so check for that too.
  495. //
  496. if ( IsBootableSystemStateBackedUp( )) {
  497. ClRtlLogPrint( LOG_NOISE, "VSS: IsBootableSystemStateBackedUp returned true\n" );
  498. g_bDoBackup = true;
  499. }
  500. goto ret;
  501. ErrorExit:
  502. CL_ASSERT( FAILED( hr ));
  503. CL_ASSERT( bRet == false );
  504. SETWRITERFAILURE( );
  505. ret:
  506. LOGFUNCTIONEXIT( OnPrepareBackup );
  507. return bRet;
  508. }
  509. //++
  510. // DESCRIPTION: CVssWriterCluster::OnPrepareSnapshot
  511. //
  512. // Callback for prepare snapshot event. Actually makes the call to backup
  513. // the cluster. Uses the target path declared in Identify so that the
  514. // ntbackup will pickup our files for us. Before doing anything the
  515. // directory is cleared out (if it exists) and the share deleted (if it
  516. // exists).
  517. //
  518. // PARAMETERS:
  519. // none
  520. //
  521. // PRE-CONDITIONS:
  522. // OnPrepareBackup was already called.
  523. //
  524. // POST-CONDITIONS:
  525. // The cluster database is backed up and the data copied to another
  526. // location for backup to save.
  527. //
  528. // RETURN VALUE:
  529. // true - continue with snapshot operation.
  530. // false - Veto the snapshot creation.
  531. //--
  532. bool STDMETHODCALLTYPE CVssWriterCluster::OnPrepareSnapshot()
  533. {
  534. NET_API_STATUS NetStatus = NERR_Success;
  535. HRESULT hr = S_OK;
  536. bool bRet = true;
  537. UNICODE_STRING ucsBackupDir;
  538. LOGFUNCTIONENTRY( OnPrepareSnapshot );
  539. if ( g_bDoBackup == false )
  540. goto ret;
  541. // Delete the share if it exists. Tolerate errors but warns for anything
  542. // except NERR_NetNameNotFound. Break if debug.
  543. //
  544. NetStatus = NetShareDel( NULL, SHARE_NAME, 0 );
  545. CL_ASSERT( NetStatus == NERR_Success || NetStatus == NERR_NetNameNotFound );
  546. if ( NetStatus != NERR_Success && NetStatus != NERR_NetNameNotFound ) {
  547. ClRtlLogPrint( LOG_UNUSUAL, "VSS: OnPrepareSnapshot: Tolerating error: %1!u! from NetShareDel\n", NetStatus );
  548. NetStatus = NERR_Success;
  549. }
  550. // Delete the directory if it exists. This can only be the case if we
  551. // prematurely exited a previous backup.
  552. //
  553. // First expand the Root, checking that our input is NULL.
  554. //
  555. StringZero( &ucsBackupDir );
  556. hr = StringCreateFromExpandedString( &ucsBackupDir, ROOT_REPAIR_DIR, MAX_PATH );
  557. if ( FAILED( hr )) {
  558. LOGERROR( hr, StringCreateFromExpandedString );
  559. bRet = false; // veto on failure
  560. goto ErrorExit;
  561. }
  562. StringAppendString( &ucsBackupDir, BACKUP_SUBDIR );
  563. StringAppendString( &ucsBackupDir, BOOTABLE_STATE_SUBDIR );
  564. StringAppendString( &ucsBackupDir, DIR_SEP_STRING APPLICATION_STRING );
  565. ClRtlLogPrint( LOG_NOISE, "VSS: OnPrepareSnapshot: Cleaning up target directory: %1!ws!\n", ucsBackupDir.Buffer );
  566. hr = CleanupTargetDirectory( ucsBackupDir.Buffer );
  567. if ( FAILED( hr ) ) {
  568. ClRtlLogPrint( LOG_UNUSUAL, "VSS: Tolerating error 0x%1!08lx! from CleanupTargetDirectory.\n", hr );
  569. hr = S_OK; // tolerate this failure.
  570. }
  571. StringFree( &ucsBackupDir );
  572. StringZero( &ucsBackupDir );
  573. hr = StringCreateFromExpandedString( &ucsBackupDir, ROOT_REPAIR_DIR, MAX_PATH );
  574. if ( FAILED( hr )) {
  575. LOGERROR( hr, StringCreateFromExpandedString );
  576. bRet = false; // veto on failure
  577. goto ErrorExit;
  578. }
  579. StringAppendString( &ucsBackupDir, BACKUP_SUBDIR );
  580. StringAppendString( &ucsBackupDir, SERVICE_STATE_SUBDIR );
  581. StringAppendString( &ucsBackupDir, DIR_SEP_STRING APPLICATION_STRING );
  582. ClRtlLogPrint( LOG_NOISE, "VSS: OnPrepareSnapshot: Cleaning up target directory: %1!ws!\n", ucsBackupDir.Buffer );
  583. hr = CleanupTargetDirectory( ucsBackupDir.Buffer );
  584. if ( FAILED( hr ) ) {
  585. ClRtlLogPrint( LOG_UNUSUAL, "VSS: Tolerating error 0x%1!08lx! from CleanupTargetDirectory.\n", hr );
  586. hr = S_OK; // tolerate this failure.
  587. }
  588. StringFree( &ucsBackupDir );
  589. hr = DoClusterDatabaseBackup( );
  590. if ( FAILED( hr ) ) {
  591. LOGERROR( hr, DoClusterDatabaseBackup );
  592. SETWRITERFAILURE( );
  593. bRet = false; // veto on failure
  594. goto ErrorExit;
  595. }
  596. goto ret;
  597. ErrorExit:
  598. CL_ASSERT( FAILED( hr ));
  599. CL_ASSERT( bRet == false );
  600. SETWRITERFAILURE( );
  601. ret:
  602. LOGFUNCTIONEXIT( OnPrepareSnapshot );
  603. return bRet;
  604. }
  605. // callback for freeze event
  606. //
  607. bool STDMETHODCALLTYPE CVssWriterCluster::OnFreeze()
  608. {
  609. LOGFUNCTIONENTRY( OnFreeze );
  610. LOGFUNCTIONEXIT( OnFreeze );
  611. return true;
  612. }
  613. // callback for thaw event
  614. //
  615. bool STDMETHODCALLTYPE CVssWriterCluster::OnThaw()
  616. {
  617. LOGFUNCTIONENTRY( OnThaw );
  618. if ( g_bDoBackup == false )
  619. goto ret;
  620. if ( g_ucsBackupPathLocal.Buffer ) {
  621. ClRtlLogPrint( LOG_NOISE, "VSS: Cleaning up target directory: %1!ws!\n", g_ucsBackupPathLocal.Buffer );
  622. HRESULT hr = CleanupTargetDirectory( g_ucsBackupPathLocal.Buffer );
  623. if ( FAILED( hr ) ) {
  624. LOGERROR( hr, CVssWriterCluster::OnThaw );
  625. ClRtlLogPrint( LOG_CRITICAL, "VSS: 0x%1!08lx! from CleanupTargetDirectory. Mapping to S_OK and continuing\n", hr );
  626. hr = S_OK; // tolerate this failure.
  627. }
  628. }
  629. if ( g_ucsClusdbBackupPathLocal.Buffer ) {
  630. ClRtlLogPrint( LOG_NOISE, "VSS: Cleaning up target directory: %1!ws!\n", g_ucsClusdbBackupPathLocal.Buffer );
  631. HRESULT hr = CleanupTargetDirectory( g_ucsClusdbBackupPathLocal.Buffer );
  632. if ( FAILED( hr ) ) {
  633. LOGERROR( hr, CVssWriterCluster::OnThaw );
  634. ClRtlLogPrint( LOG_CRITICAL, "VSS: 0x%1!08lx! from CleanupTargetDirectory. Mapping to S_OK and continuing\n", hr );
  635. hr = S_OK; // tolerate this failure.
  636. }
  637. }
  638. // Free the buffer if non-NULL.
  639. //
  640. StringFree ( &g_ucsBackupPathLocal );
  641. StringFree ( &g_ucsClusdbBackupPathLocal );
  642. LOGFUNCTIONEXIT( OnThaw );
  643. ret:
  644. return true;
  645. }
  646. // callback if current sequence is aborted
  647. //
  648. bool STDMETHODCALLTYPE CVssWriterCluster::OnAbort()
  649. {
  650. LOGFUNCTIONENTRY( OnAbort );
  651. bool bRet = OnThaw( );
  652. LOGFUNCTIONEXIT( OnAbort );
  653. return bRet;
  654. }
  655. //++
  656. // DESCRIPTION: DoClusterDatabaseBackup
  657. //
  658. // Perform the backup of the cluster database. This function wraps
  659. // FmBackupClusterDatabase which does the right thing to do the cluster
  660. // backup. This function first creates a directory that will serve as the
  661. // destination for the backup. Next it creates a network share to point
  662. // to this directory and starts the cluster backup. After this is done it
  663. // cleans up.
  664. //
  665. // PARAMETERS:
  666. // none
  667. //
  668. // PRE-CONDITIONS:
  669. // . Called only from CVssWriterCluster::OnPrepareSnapshot.
  670. // . We must be the only call to backup in progress on this machine (the
  671. // share names will clash otherwise and clustering may not behave well
  672. // with multiple FmBackupClusterDatabase calls it the same time).
  673. //
  674. // POST-CONDITIONS:
  675. // Cluster database backed up to another location, ready for the backup tool to copy.
  676. //
  677. // RETURN VALUE:
  678. // S_OK - all went well.
  679. // Error status from creating directories or net shares or from cluster backup.
  680. //--
  681. static HRESULT DoClusterDatabaseBackup( )
  682. {
  683. LOGFUNCTIONENTRY( DoClusterDatabaseBackup );
  684. HRESULT hr = S_OK;
  685. bool bNetShareAdded = false;
  686. SHARE_INFO_502 ShareInfo;
  687. UNICODE_STRING ucsComputerName;
  688. UNICODE_STRING ucsBackupPathNetwork;
  689. UNICODE_STRING ucsCheckpointFile, ucsClusdbTargetFile;
  690. NET_API_STATUS NetStatus;
  691. HANDLE hFind = INVALID_HANDLE_VALUE;
  692. WIN32_FIND_DATA FindData;
  693. StringZero( &ucsComputerName );
  694. StringZero( &g_ucsBackupPathLocal );
  695. StringZero( &ucsBackupPathNetwork );
  696. StringZero( &g_ucsClusdbBackupPathLocal );
  697. StringZero( &ucsCheckpointFile );
  698. StringZero( &ucsClusdbTargetFile );
  699. // Create the directories and set the attributes and security and stuff.
  700. // Set g_ucsBackupPathLocal to the directory created.
  701. //
  702. hr = CreateTargetDirectory( &g_ucsBackupPathLocal, TRUE );
  703. if ( FAILED (hr )) {
  704. LOGERROR( hr, CreateTargetDirectory );
  705. goto ErrorExit;
  706. }
  707. #ifdef DBG
  708. {
  709. // Check that the directory does exist.
  710. //
  711. DWORD dwFileAttributes = GetFileAttributesW( g_ucsBackupPathLocal.Buffer );
  712. hr = GET_HR_FROM_BOOL( dwFileAttributes != -1 );
  713. ClRtlLogPrint( LOG_NOISE, "VSS: GetFileAttributes(1) returned 0x%1!08lx! for path: %2!ws!\n",
  714. hr, g_ucsBackupPathLocal.Buffer );
  715. }
  716. #endif
  717. hr = StringAllocate (&ucsComputerName,
  718. (MAX_COMPUTERNAME_LENGTH * sizeof (WCHAR)) + sizeof (UNICODE_NULL));
  719. if ( FAILED( hr )) {
  720. LOGERROR( hr, StringAllocate );
  721. goto ErrorExit;
  722. }
  723. DWORD dwNameLength = ucsComputerName.MaximumLength / sizeof (WCHAR);
  724. hr = GET_HR_FROM_BOOL( GetComputerNameW( ucsComputerName.Buffer, &dwNameLength ));
  725. if ( FAILED ( hr )) {
  726. LOGERROR( hr, GetComputerNameW );
  727. goto ErrorExit;
  728. }
  729. ucsComputerName.Length = (USHORT) (dwNameLength * sizeof (WCHAR));
  730. hr = StringAllocate (&ucsBackupPathNetwork,
  731. (USHORT) (sizeof (L'\\')
  732. + sizeof (L'\\')
  733. + ucsComputerName.Length
  734. + sizeof (L'\\')
  735. + ( wcslen( SHARE_NAME ) * sizeof( WCHAR ) )
  736. + sizeof (UNICODE_NULL)));
  737. if ( FAILED ( hr )) {
  738. LOGERROR( hr, GetComputerNameW );
  739. goto ErrorExit;
  740. }
  741. ClRtlLogPrint( LOG_NOISE, "VSS: backup path network size: %1!u!\n", ucsBackupPathNetwork.Length );
  742. //
  743. // Should we uniquify the directory name at all here
  744. // to cater for the possiblity that we may be involved
  745. // in more than one snapshot at a time?
  746. //
  747. StringAppendString( &ucsBackupPathNetwork, L"\\\\" );
  748. StringAppendString( &ucsBackupPathNetwork, &ucsComputerName );
  749. StringAppendString( &ucsBackupPathNetwork, L"\\" );
  750. StringAppendString( &ucsBackupPathNetwork, SHARE_NAME );
  751. ClRtlLogPrint( LOG_NOISE, "VSS: backup path network: %1!ws!\n", ucsBackupPathNetwork.Buffer );
  752. ZeroMemory( &ShareInfo, sizeof( ShareInfo ));
  753. ShareInfo.shi502_netname = SHARE_NAME;
  754. ShareInfo.shi502_type = STYPE_DISKTREE;
  755. ShareInfo.shi502_permissions = ACCESS_READ | ACCESS_WRITE | ACCESS_CREATE;
  756. ShareInfo.shi502_max_uses = 1;
  757. ShareInfo.shi502_path = g_ucsBackupPathLocal.Buffer;
  758. #ifdef DBG
  759. {
  760. // Check that the directory does exist.
  761. //
  762. DWORD dwFileAttributes = GetFileAttributesW( g_ucsBackupPathLocal.Buffer );
  763. hr = GET_HR_FROM_BOOL( dwFileAttributes != -1 );
  764. ClRtlLogPrint( LOG_NOISE, "VSS: GetFileAttributes(2) returned 0x%1!08lx! for path: %2!ws!\n",
  765. hr, g_ucsBackupPathLocal.Buffer );
  766. }
  767. #endif
  768. //
  769. // Make sure to try to delete the share first in case for some reason it exists.
  770. //
  771. NetStatus = NetShareDel( NULL, SHARE_NAME, 0 );
  772. ClRtlLogPrint( LOG_NOISE, "VSS: NetShareDel returned: %1!u!\n", NetStatus );
  773. if ( NetStatus == NERR_NetNameNotFound )
  774. NetStatus = NERR_Success;
  775. CL_ASSERT( NetStatus == NERR_Success );
  776. #ifdef DBG
  777. {
  778. // Check that the directory does exist.
  779. //
  780. DWORD dwFileAttributes = GetFileAttributesW( g_ucsBackupPathLocal.Buffer );
  781. hr = GET_HR_FROM_BOOL( dwFileAttributes != -1 );
  782. ClRtlLogPrint( LOG_NOISE, "VSS: GetFileAttributes(3) returned 0x%1!08lx! for path: %2!ws!\n",
  783. hr, g_ucsBackupPathLocal.Buffer );
  784. }
  785. #endif
  786. ClRtlLogPrint( LOG_NOISE, "VSS: NetShareAdd: Adding share: %1!ws! with path: %2!ws!\n", SHARE_NAME, g_ucsBackupPathLocal.Buffer );
  787. NetStatus = NetShareAdd( NULL, 502, (LPBYTE)(&ShareInfo), NULL );
  788. ClRtlLogPrint( LOG_NOISE, "VSS: NetShareAdd completed: %1!u!\n", NetStatus );
  789. if ( NetStatus != NERR_Success ) {
  790. LOGERROR( NetStatus, NetShareAdd );
  791. if ( NetStatus == NERR_DuplicateShare ) {
  792. ClRtlLogPrint( LOG_NOISE, "VSS: Mapping NERR_DuplicateShare to success\n" );
  793. NetStatus = NERR_Success;
  794. } else {
  795. hr = HRESULT_FROM_WIN32( NetStatus );
  796. goto ErrorExit;
  797. }
  798. }
  799. bNetShareAdded = true;
  800. #ifdef DBG
  801. {
  802. // Check that the directory does exist.
  803. //
  804. DWORD dwFileAttributes = GetFileAttributesW( g_ucsBackupPathLocal.Buffer );
  805. hr = GET_HR_FROM_BOOL( dwFileAttributes != -1 );
  806. ClRtlLogPrint( LOG_NOISE, "VSS: GetFileAttributes returned 0x%1!08lx! for path: %2!ws!\n",
  807. hr, g_ucsBackupPathLocal.Buffer );
  808. }
  809. #endif
  810. // If we are not logging to the quorum log then we don't do the backup.
  811. //
  812. if ( CsNoQuorumLogging || CsUserTurnedOffQuorumLogging ) {
  813. ClRtlLogPrint( LOG_NOISE, "VSS: Quorum logging is turned off. Not attempting backup.\n" );
  814. //
  815. // CMCM!
  816. // We could opt to take a checkpoint and then setup alternate
  817. // path to ensure it is copied over CLUSDB on restore.
  818. //
  819. hr = S_OK;
  820. } else {
  821. ClRtlLogPrint( LOG_NOISE, "VSS: Calling FmBackupClusterDatabase with path: %1!ws!\n", ucsBackupPathNetwork.Buffer );
  822. DWORD dwStatus = FmBackupClusterDatabase( ucsBackupPathNetwork.Buffer );
  823. hr = HRESULT_FROM_WIN32( dwStatus );
  824. ClRtlLogPrint( LOG_NOISE, "VSS: FmBackupClusterDatabase completed. hr: 0x%1!08lx! \n", hr );
  825. if ( FAILED( hr ) ) {
  826. LOGERROR( hr, FmBackupClusterDatabase );
  827. goto ErrorExit;
  828. }
  829. //
  830. // CAUTION: DO NOT CHANGE THE DROP LOCATION OF CLUSDB WITHOUT CONSULTING WITH NTBACKUP
  831. // OWNERS. THIS LOCATION %SystemRoot%\Repair\Backup\ServiceState\ClusterDatabase is a
  832. // MUTUALLY AGREED UPON LOCATION WHERE NTBACKUP.EXE WILL LOOK FOR CLUSDB.
  833. //
  834. // Pick up the checkpoint file and copy it as CLUSDB to the service state subdir
  835. //
  836. hr = StringAllocate( &ucsCheckpointFile,
  837. ( USHORT ) ( ( lstrlen ( g_ucsBackupPathLocal.Buffer ) +
  838. lstrlen ( L"\\chk*.tmp" ) +
  839. 1 ) * sizeof ( WCHAR ) ) );
  840. if ( FAILED( hr )) {
  841. LOGERROR( hr, StringAllocate );
  842. goto ErrorExit;
  843. }
  844. StringAppendString( &ucsCheckpointFile, g_ucsBackupPathLocal.Buffer );
  845. StringAppendString( &ucsCheckpointFile, L"\\chk*.tmp" );
  846. hFind = FindFirstFile ( ucsCheckpointFile.Buffer, &FindData );
  847. StringFree ( &ucsCheckpointFile );
  848. if ( hFind == INVALID_HANDLE_VALUE ) {
  849. hr = HRESULT_FROM_WIN32( GetLastError() );
  850. LOGERROR( hr, FindFirstFile );
  851. goto ErrorExit;
  852. }
  853. hr = StringAllocate( &ucsCheckpointFile,
  854. ( USHORT ) ( ( lstrlen ( g_ucsBackupPathLocal.Buffer ) +
  855. lstrlen ( DIR_SEP_STRING ) +
  856. lstrlen ( FindData.cFileName ) +
  857. 1 ) * sizeof ( WCHAR ) ) );
  858. if ( FAILED( hr )) {
  859. LOGERROR( hr, StringAllocate );
  860. goto ErrorExit;
  861. }
  862. hr = CreateTargetDirectory( &g_ucsClusdbBackupPathLocal, FALSE );
  863. if ( FAILED (hr )) {
  864. LOGERROR( hr, CreateTargetDirectory );
  865. StringFree ( &ucsCheckpointFile );
  866. goto ErrorExit;
  867. }
  868. StringAppendString( &ucsCheckpointFile, g_ucsBackupPathLocal.Buffer );
  869. StringAppendString( &ucsCheckpointFile, DIR_SEP_STRING );
  870. StringAppendString( &ucsCheckpointFile, FindData.cFileName );
  871. hr = StringAllocate( &ucsClusdbTargetFile,
  872. ( USHORT ) ( ( lstrlen ( g_ucsClusdbBackupPathLocal.Buffer ) +
  873. lstrlen ( DIR_SEP_STRING ) +
  874. lstrlen ( CLUSTER_DATABASE_NAME ) +
  875. 1 ) * sizeof ( WCHAR ) ) );
  876. if ( FAILED( hr )) {
  877. StringFree ( &ucsCheckpointFile );
  878. LOGERROR( hr, StringAllocate );
  879. goto ErrorExit;
  880. }
  881. StringAppendString( &ucsClusdbTargetFile, g_ucsClusdbBackupPathLocal.Buffer );
  882. StringAppendString( &ucsClusdbTargetFile, DIR_SEP_STRING );
  883. StringAppendString( &ucsClusdbTargetFile, CLUSTER_DATABASE_NAME );
  884. if ( !CopyFile ( ucsCheckpointFile.Buffer, ucsClusdbTargetFile.Buffer, FALSE ) ) {
  885. hr = HRESULT_FROM_WIN32( GetLastError() );
  886. LOGERROR( hr, CopyFile );
  887. StringFree ( &ucsCheckpointFile );
  888. StringFree ( &ucsClusdbTargetFile );
  889. goto ErrorExit;
  890. }
  891. StringFree ( &ucsCheckpointFile );
  892. StringFree ( &ucsClusdbTargetFile );
  893. }
  894. goto ret;
  895. ErrorExit:
  896. CL_ASSERT( FAILED( hr ));
  897. ret:
  898. #ifdef DBG
  899. ClRtlLogPrint( LOG_NOISE, "VSS:\n" );
  900. ClRtlLogPrint( LOG_NOISE, "VSS: DEBUG - sleeping for 30s. This would be a good time to test killing backup in progress...\n" );
  901. ClRtlLogPrint( LOG_NOISE, "VSS:\n" );
  902. Sleep( 30*1000 );
  903. #endif
  904. // Common cleanup for success or failure.
  905. //
  906. if ( bNetShareAdded ) {
  907. NetStatus = NetShareDel (NULL, SHARE_NAME, 0);
  908. ClRtlLogPrint( LOG_NOISE, "VSS: NetShareDel returned: %1!u!\n", NetStatus );
  909. if ( NetStatus == NERR_NetNameNotFound )
  910. NetStatus = NERR_Success;
  911. CL_ASSERT( NetStatus == NERR_Success );
  912. }
  913. // Cleanup strings but leave the local path so we can cleanup the files later.
  914. //
  915. StringFree( &ucsComputerName );
  916. StringFree( &ucsBackupPathNetwork );
  917. if ( hFind != INVALID_HANDLE_VALUE ) FindClose ( hFind );
  918. LOGFUNCTIONEXIT( DoClusterDatabaseBackup );
  919. return hr;
  920. }
  921. //++
  922. // DESCRIPTION: ConstructSecurityAttributes
  923. //
  924. // Routines to construct and cleanup a security descriptor which can be
  925. // applied to limit access to an object to member of either the
  926. // Administrators or Backup Operators group.
  927. //
  928. // PARAMETERS:
  929. // psaSecurityAttributes - Pointer to a SecurityAttributes structure which
  930. // has already been setup to point to a blank
  931. // security descriptor.
  932. // bIncludeBackupOperator - Whether or not to include an ACE to grant
  933. // BackupOperator access
  934. //
  935. // PRE-CONDITIONS:
  936. // None.
  937. //
  938. // POST-CONDITIONS:
  939. // Security attributes created that are suitable for backup directory
  940. //
  941. // RETURN VALUE:
  942. // S_OK - Attributes created OK.
  943. // Error from setting up attributes or SID or ACL.
  944. //--
  945. static HRESULT ConstructSecurityAttributes( PSECURITY_ATTRIBUTES psaSecurityAttributes,
  946. BOOL bIncludeBackupOperator )
  947. {
  948. HRESULT hr = NOERROR;
  949. DWORD dwStatus;
  950. DWORD dwAccessMask = FILE_ALL_ACCESS;
  951. PSID psidBackupOperators = NULL;
  952. PSID psidAdministrators = NULL;
  953. PACL paclDiscretionaryAcl = NULL;
  954. SID_IDENTIFIER_AUTHORITY sidNtAuthority = SECURITY_NT_AUTHORITY;
  955. EXPLICIT_ACCESS eaExplicitAccess [2];
  956. //
  957. // Initialise the security descriptor.
  958. //
  959. hr = GET_HR_FROM_BOOL( InitializeSecurityDescriptor( psaSecurityAttributes->lpSecurityDescriptor,
  960. SECURITY_DESCRIPTOR_REVISION ));
  961. if ( FAILED( hr )) {
  962. LOGERROR( hr, InitializeSecurityDescriptor );
  963. goto ErrorExit;
  964. }
  965. if ( bIncludeBackupOperator ) {
  966. //
  967. // Create a SID for the Backup Operators group.
  968. //
  969. hr = GET_HR_FROM_BOOL( AllocateAndInitializeSid( &sidNtAuthority,
  970. 2,
  971. SECURITY_BUILTIN_DOMAIN_RID,
  972. DOMAIN_ALIAS_RID_BACKUP_OPS,
  973. 0, 0, 0, 0, 0, 0,
  974. &psidBackupOperators ));
  975. if ( FAILED( hr )) {
  976. LOGERROR( hr, AllocateAndInitializeSid );
  977. goto ErrorExit;
  978. }
  979. }
  980. //
  981. // Create a SID for the Administrators group.
  982. //
  983. hr = GET_HR_FROM_BOOL( AllocateAndInitializeSid( &sidNtAuthority,
  984. 2,
  985. SECURITY_BUILTIN_DOMAIN_RID,
  986. DOMAIN_ALIAS_RID_ADMINS,
  987. 0, 0, 0, 0, 0, 0,
  988. &psidAdministrators ));
  989. if ( FAILED( hr )) {
  990. LOGERROR( hr, InitializeSecurityDescriptor );
  991. goto ErrorExit;
  992. }
  993. //
  994. // Initialize the array of EXPLICIT_ACCESS structures for an
  995. // ACEs we are setting.
  996. //
  997. // The first ACE allows the Backup Operators group full access
  998. // and the second, allowa the Administrators group full
  999. // access.
  1000. //
  1001. eaExplicitAccess[0].grfAccessPermissions = dwAccessMask;
  1002. eaExplicitAccess[0].grfAccessMode = SET_ACCESS;
  1003. eaExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  1004. eaExplicitAccess[0].Trustee.pMultipleTrustee = NULL;
  1005. eaExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  1006. eaExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  1007. eaExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS;
  1008. eaExplicitAccess[0].Trustee.ptstrName =( LPTSTR ) psidAdministrators;
  1009. if ( bIncludeBackupOperator ) {
  1010. eaExplicitAccess[1].grfAccessPermissions = dwAccessMask;
  1011. eaExplicitAccess[1].grfAccessMode = SET_ACCESS;
  1012. eaExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  1013. eaExplicitAccess[1].Trustee.pMultipleTrustee = NULL;
  1014. eaExplicitAccess[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  1015. eaExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  1016. eaExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_ALIAS;
  1017. eaExplicitAccess[1].Trustee.ptstrName =( LPTSTR ) psidBackupOperators;
  1018. }
  1019. //
  1020. // Create a new ACL that contains the new ACEs.
  1021. //
  1022. dwStatus = SetEntriesInAcl( bIncludeBackupOperator ? 2 : 1,
  1023. eaExplicitAccess,
  1024. NULL,
  1025. &paclDiscretionaryAcl );
  1026. hr = HRESULT_FROM_WIN32( dwStatus );
  1027. if ( FAILED( hr )) {
  1028. LOGERROR( hr, SetEntriesInAcl );
  1029. goto ErrorExit;
  1030. }
  1031. //
  1032. // Add the ACL to the security descriptor.
  1033. //
  1034. hr = GET_HR_FROM_BOOL( SetSecurityDescriptorDacl( psaSecurityAttributes->lpSecurityDescriptor,
  1035. true,
  1036. paclDiscretionaryAcl,
  1037. false ));
  1038. if ( FAILED( hr )) {
  1039. LOGERROR( hr, SetSecurityDescriptorDacl );
  1040. goto ErrorExit;
  1041. }
  1042. paclDiscretionaryAcl = NULL;
  1043. goto ret;
  1044. ErrorExit:
  1045. CL_ASSERT( FAILED( hr ));
  1046. ret:
  1047. // Cleanup (some may be NULL)
  1048. FreeSid( psidAdministrators );
  1049. FreeSid( psidBackupOperators );
  1050. LocalFree( paclDiscretionaryAcl );
  1051. return hr;
  1052. }
  1053. //++
  1054. // DESCRIPTION: CleanupSecurityAttributes
  1055. //
  1056. // Deallocate the ACL if present with the security attributes.
  1057. //
  1058. // PARAMETERS:
  1059. // psaSecurityAttributes - Pointer to a SecurityAttributes structure which
  1060. // has already been setup to point to a blank
  1061. // security descriptor.
  1062. //
  1063. // PRE-CONDITIONS:
  1064. // psaSecurityAttributes points to security attributes as allocated by
  1065. // ConstructSecurityAttributes.
  1066. //
  1067. // POST-CONDITIONS:
  1068. // Memory freed if it was in use.
  1069. //
  1070. // RETURN VALUE:
  1071. // None.
  1072. //--
  1073. static VOID CleanupSecurityAttributes( PSECURITY_ATTRIBUTES psaSecurityAttributes )
  1074. {
  1075. BOOL bDaclPresent = false;
  1076. BOOL bDaclDefaulted = true;
  1077. PACL paclDiscretionaryAcl = NULL;
  1078. BOOL bSucceeded = GetSecurityDescriptorDacl( psaSecurityAttributes->lpSecurityDescriptor,
  1079. &bDaclPresent,
  1080. &paclDiscretionaryAcl,
  1081. &bDaclDefaulted );
  1082. if ( bSucceeded && bDaclPresent && !bDaclDefaulted && ( paclDiscretionaryAcl != NULL )) {
  1083. LocalFree( paclDiscretionaryAcl );
  1084. }
  1085. }
  1086. //++
  1087. // DESCRIPTION: CleanupTargetDirectory
  1088. //
  1089. // Deletes all the files present in the directory pointed at by the target
  1090. // path member variable if not NULL. It will also remove the target
  1091. // directory itself, eg for a target path of c:\dir1\dir2 all files under
  1092. // dir2 will be removed and then dir2 itself will be deleted.
  1093. //
  1094. // PARAMETERS:
  1095. // pwszTargetPath - full path to the directory to cleanup.
  1096. //
  1097. // PRE-CONDITIONS:
  1098. // pwszTargetPath non NULL.
  1099. //
  1100. // POST-CONDITIONS:
  1101. // Directory and contained files deleted.
  1102. //
  1103. // RETURN VALUE:
  1104. // S_OK - Directory and contained files all cleaned up OK.
  1105. // Error status from RemoveDirectoryTree or from GetFileAttributesW
  1106. //--
  1107. static HRESULT CleanupTargetDirectory( LPCWSTR pwszTargetPath )
  1108. {
  1109. LOGFUNCTIONENTRY( CleanupTargetDirectory );
  1110. HRESULT hr = NOERROR;
  1111. DWORD dwFileAttributes = 0;
  1112. BOOL bSucceeded;
  1113. WCHAR wszTempBuffer [50];
  1114. UNICODE_STRING ucsTargetPath;
  1115. UNICODE_STRING ucsTargetPathAlternateName;
  1116. CL_ASSERT( pwszTargetPath != NULL );
  1117. StringZero( &ucsTargetPath );
  1118. StringZero( &ucsTargetPathAlternateName );
  1119. //
  1120. // Create strings with extra space for appending onto later.
  1121. //
  1122. hr = StringCreateFromExpandedString( &ucsTargetPath, pwszTargetPath, MAX_PATH );
  1123. if ( FAILED( hr )) {
  1124. LOGERROR( hr, StringCreateFromExpandedString );
  1125. goto ErrorExit;
  1126. }
  1127. hr = StringCreateFromString( &ucsTargetPathAlternateName, &ucsTargetPath, MAX_PATH );
  1128. if ( FAILED( hr )) {
  1129. LOGERROR( hr, StringCreateFromString );
  1130. goto ErrorExit;
  1131. }
  1132. dwFileAttributes = GetFileAttributesW( ucsTargetPath.Buffer );
  1133. hr = GET_HR_FROM_BOOL( dwFileAttributes != -1 );
  1134. if (( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ))
  1135. || ( hr == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ))) {
  1136. hr = NOERROR;
  1137. dwFileAttributes = 0;
  1138. }
  1139. if ( FAILED( hr )) {
  1140. LOGERROR( hr, GetFileAttributesW );
  1141. goto ErrorExit;
  1142. }
  1143. //
  1144. // If there is a file there then blow it away, or if it's
  1145. // a directory, blow it and all it's contents away. This
  1146. // is our directory and no one but us gets to play there.
  1147. // The random rename directory could exist but it's only for cleanup anyway...
  1148. //
  1149. hr = RemoveDirectoryTree( &ucsTargetPath );
  1150. if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ))
  1151. hr = S_OK;
  1152. if ( FAILED( hr )) {
  1153. srand( (unsigned) GetTickCount( ));
  1154. _itow( rand (), wszTempBuffer, 16 );
  1155. StringAppendString( &ucsTargetPathAlternateName, wszTempBuffer );
  1156. bSucceeded = MoveFileW( ucsTargetPath.Buffer, ucsTargetPathAlternateName.Buffer );
  1157. if (bSucceeded) {
  1158. ClRtlLogPrint( LOG_UNUSUAL, "VSS: CleanupTargetDirectory failed to delete %1!ws! with hr: 0x%2!08lx! so renamed to %3!ws!\n",
  1159. ucsTargetPath.Buffer,
  1160. hr,
  1161. ucsTargetPathAlternateName.Buffer );
  1162. } else {
  1163. ClRtlLogPrint( LOG_UNUSUAL, "VSS: CleanupTargetDirectory failed to delete %1!ws! with hr: 0x%2!08lx!"
  1164. " failed to rename to %3!ws! with status 0x%4!08lx!\n",
  1165. ucsTargetPath.Buffer,
  1166. hr,
  1167. ucsTargetPathAlternateName.Buffer,
  1168. GET_HR_FROM_BOOL (bSucceeded) );
  1169. }
  1170. }
  1171. goto ret;
  1172. ErrorExit:
  1173. CL_ASSERT( FAILED( hr ));
  1174. ret:
  1175. StringFree( &ucsTargetPathAlternateName );
  1176. StringFree( &ucsTargetPath );
  1177. LOGFUNCTIONEXIT( CleanupTargetDirectory );
  1178. return hr;
  1179. }
  1180. //++
  1181. // DESCRIPTION: RemoveDirectoryTree
  1182. //
  1183. // Deletes all the sub-directories and files in the specified directory
  1184. // and then deletes the directory itself.
  1185. //
  1186. // PARAMETERS:
  1187. // pucsDirectoryPath - pointer to the directory
  1188. //
  1189. // PRE-CONDITIONS:
  1190. // Called only from CleanupTargetDirectory
  1191. //
  1192. // POST-CONDITIONS:
  1193. // Directory tree deleted.
  1194. //
  1195. // RETURN VALUE:
  1196. // S_OK - Directory tree deleted.
  1197. // Error status from deleting directory or from allocating strings.
  1198. //--
  1199. static HRESULT RemoveDirectoryTree( PUNICODE_STRING pucsDirectoryPath )
  1200. {
  1201. LOGFUNCTIONENTRY( RemoveDirectoryTree );
  1202. HRESULT hr = NOERROR;
  1203. HANDLE hFileScan = INVALID_HANDLE_VALUE;
  1204. DWORD dwSubDirectoriesEntered = 0;
  1205. USHORT usCurrentPathCursor = 0;
  1206. PWCHAR pwchLastSlash = NULL;
  1207. bool bContinue = true;
  1208. UNICODE_STRING ucsCurrentPath;
  1209. WIN32_FIND_DATAW FileFindData;
  1210. StringZero (&ucsCurrentPath);
  1211. LOGUNICODESTRING( *pucsDirectoryPath );
  1212. // Create the string with enough extra characters to allow all the
  1213. // appending later on!
  1214. //
  1215. hr = StringCreateFromString (&ucsCurrentPath, pucsDirectoryPath, MAX_PATH);
  1216. if ( FAILED ( hr )) {
  1217. LOGERROR( hr, StringCreateFromString );
  1218. goto ErrorExit;
  1219. }
  1220. pwchLastSlash = wcsrchr (ucsCurrentPath.Buffer, DIR_SEP_CHAR);
  1221. usCurrentPathCursor = (USHORT)(pwchLastSlash - ucsCurrentPath.Buffer) + 1;
  1222. while ( SUCCEEDED( hr ) && bContinue ) {
  1223. if ( HandleInvalid( hFileScan )) {
  1224. //
  1225. // No valid scan handle so start a new scan
  1226. //
  1227. ClRtlLogPrint( LOG_NOISE, "VSS: Starting scan: %1!ws!\n", ucsCurrentPath.Buffer );
  1228. hFileScan = FindFirstFileW( ucsCurrentPath.Buffer, &FileFindData );
  1229. hr = GET_HR_FROM_HANDLE( hFileScan );
  1230. if ( SUCCEEDED( hr )) {
  1231. StringTruncate( &ucsCurrentPath, usCurrentPathCursor );
  1232. StringAppendString( &ucsCurrentPath, FileFindData.cFileName );
  1233. }
  1234. } else {
  1235. //
  1236. // Continue with the existing scan
  1237. //
  1238. hr = GET_HR_FROM_BOOL( FindNextFileW( hFileScan, &FileFindData ));
  1239. if (SUCCEEDED( hr )) {
  1240. StringTruncate( &ucsCurrentPath, usCurrentPathCursor );
  1241. StringAppendString( &ucsCurrentPath, FileFindData.cFileName );
  1242. } else if ( hr == HRESULT_FROM_WIN32( ERROR_NO_MORE_FILES )) {
  1243. FindClose( hFileScan );
  1244. hFileScan = INVALID_HANDLE_VALUE;
  1245. if (dwSubDirectoriesEntered > 0) {
  1246. //
  1247. // This is a scan of a sub-directory that is now
  1248. // complete so delete the sub-directory itself.
  1249. //
  1250. StringTruncate( &ucsCurrentPath, usCurrentPathCursor - 1 );
  1251. hr = GET_HR_FROM_BOOL( QfsRemoveDirectory( ucsCurrentPath.Buffer ));
  1252. dwSubDirectoriesEntered--;
  1253. }
  1254. if ( dwSubDirectoriesEntered == 0) {
  1255. //
  1256. // We are back to where we started except that the
  1257. // requested directory is now gone. Time to leave.
  1258. //
  1259. bContinue = false;
  1260. hr = NOERROR;
  1261. } else {
  1262. //
  1263. // Move back up one directory level, reset the cursor
  1264. // and prepare the path buffer to begin a new scan.
  1265. //
  1266. pwchLastSlash = wcsrchr( ucsCurrentPath.Buffer, DIR_SEP_CHAR );
  1267. usCurrentPathCursor =( USHORT )( pwchLastSlash - ucsCurrentPath.Buffer ) + 1;
  1268. StringTruncate( &ucsCurrentPath, usCurrentPathCursor );
  1269. StringAppendString( &ucsCurrentPath, L"*" );
  1270. }
  1271. //
  1272. // No files to be processed on this pass so go back and try to
  1273. // find another or leave the loop as we've finished the task.
  1274. //
  1275. continue;
  1276. }
  1277. }
  1278. if (SUCCEEDED( hr )) {
  1279. if ( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  1280. SetFileAttributesW( ucsCurrentPath.Buffer,
  1281. FileFindData.dwFileAttributes ^ (FILE_ATTRIBUTE_READONLY) );
  1282. }
  1283. if ( !NameIsDotOrDotDot( FileFindData.cFileName )) {
  1284. if (( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) ||
  1285. !( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )) {
  1286. ClRtlLogPrint( LOG_NOISE, "VSS: RemoveDirectoryTree: Deleting file: %1!ws!\n", ucsCurrentPath.Buffer );
  1287. hr = GET_HR_FROM_BOOL( QfsDeleteFile( ucsCurrentPath.Buffer ) );
  1288. } else {
  1289. ClRtlLogPrint( LOG_NOISE, "VSS: RemoveDirectoryTree: RemoveDirectory: %1!ws!\n", ucsCurrentPath.Buffer );
  1290. hr = GET_HR_FROM_BOOL( QfsRemoveDirectory( ucsCurrentPath.Buffer ));
  1291. if (hr == HRESULT_FROM_WIN32( ERROR_DIR_NOT_EMPTY )) {
  1292. ClRtlLogPrint( LOG_NOISE, "VSS: RemoveDirectoryTree: dir not empty. Restarting scan.\n" );
  1293. //
  1294. // The directory wasn't empty so move down one level,
  1295. // close the old scan and start a new one.
  1296. //
  1297. hr = S_OK;
  1298. FindClose( hFileScan );
  1299. hFileScan = INVALID_HANDLE_VALUE;
  1300. StringAppendString( &ucsCurrentPath, DIR_SEP_STRING L"*" );
  1301. usCurrentPathCursor =( ucsCurrentPath.Length / sizeof (WCHAR) ) - 1;
  1302. dwSubDirectoriesEntered++;
  1303. }
  1304. }
  1305. }
  1306. }
  1307. LOGUNICODESTRING( ucsCurrentPath );
  1308. } // while
  1309. if ( FAILED( hr )) {
  1310. ClRtlLogPrint( LOG_NOISE, "VSS: RemoveDirectoryTree: exited while loop due to failed hr: 0x%1!08lx!\n", hr );
  1311. goto ErrorExit;
  1312. }
  1313. goto ret;
  1314. ErrorExit:
  1315. CL_ASSERT( FAILED( hr ));
  1316. ret:
  1317. if ( !HandleInvalid( hFileScan ))
  1318. FindClose( hFileScan );
  1319. StringFree( &ucsCurrentPath );
  1320. return hr;
  1321. }
  1322. //////////////////////////////////////////////////////////////////////////
  1323. // Some useful UNICODE string stuff.
  1324. //////////////////////////////////////////////////////////////////////////
  1325. //
  1326. static HRESULT StringAllocate( PUNICODE_STRING pucsString, USHORT usMaximumStringLengthInBytes )
  1327. {
  1328. HRESULT hr = NOERROR;
  1329. LPVOID pvBuffer = NULL;
  1330. SIZE_T cActualLength = 0;
  1331. pvBuffer = HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, usMaximumStringLengthInBytes );
  1332. hr = GET_HR_FROM_POINTER( pvBuffer );
  1333. if ( FAILED (hr )) {
  1334. LOGERROR( hr, StringAllocate );
  1335. goto ErrorExit;
  1336. }
  1337. pucsString->Buffer = (PWCHAR)pvBuffer;
  1338. pucsString->Length = 0;
  1339. pucsString->MaximumLength = usMaximumStringLengthInBytes;
  1340. cActualLength = HeapSize ( GetProcessHeap( ), 0, pvBuffer );
  1341. if ( ( cActualLength <= MAXUSHORT ) && ( cActualLength > usMaximumStringLengthInBytes ))
  1342. pucsString->MaximumLength = (USHORT) cActualLength;
  1343. goto ret;
  1344. ErrorExit:
  1345. CL_ASSERT( FAILED( hr ));
  1346. ret:
  1347. ClRtlLogPrint( LOG_NOISE, "VSS: Allocated string at: 0x%1!08lx! Length: %2!u! MaxLength: %3!u!\n",
  1348. pucsString->Buffer, pucsString->Length, pucsString->MaximumLength );
  1349. return hr;
  1350. }
  1351. static void StringFree( PUNICODE_STRING pucsString )
  1352. {
  1353. HRESULT hr = NOERROR;
  1354. CL_ASSERT( pucsString->Length <= pucsString->MaximumLength );
  1355. CL_ASSERT( ( pucsString->Buffer == NULL) ? pucsString->Length == 0 : pucsString->MaximumLength > 0 );
  1356. if ( pucsString->Buffer == NULL ) {
  1357. ClRtlLogPrint( LOG_UNUSUAL, "VSS: StringFree. Attempt to free NULL buffer.\n" );
  1358. return;
  1359. }
  1360. ClRtlLogPrint( LOG_NOISE, "VSS: Freeing string at: %1!ws!\n", pucsString->Buffer );
  1361. ClRtlLogPrint( LOG_NOISE, "VSS: Freeing string at: 0x%1!08lx! Length: %2!u! MaxLength: %3!u!\n",
  1362. pucsString->Buffer, pucsString->Length, pucsString->MaximumLength );
  1363. hr = GET_HR_FROM_BOOL( HeapFree( GetProcessHeap( ), 0, pucsString->Buffer ));
  1364. CL_ASSERT ( SUCCEEDED( hr ));
  1365. pucsString->Buffer = NULL;
  1366. pucsString->Length = 0;
  1367. pucsString->MaximumLength = 0;
  1368. }
  1369. static HRESULT StringCreateFromExpandedString( PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars)
  1370. {
  1371. HRESULT hr = NOERROR;
  1372. DWORD dwStringLength;
  1373. //
  1374. // Remember, ExpandEnvironmentStringsW () includes the terminating null in the response.
  1375. //
  1376. dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString, NULL, 0) + dwExtraChars;
  1377. hr = GET_HR_FROM_BOOL( dwStringLength != 0 );
  1378. if ( FAILED ( hr )) {
  1379. LOGERROR( hr, ExpandEnvironmentStringsW );
  1380. goto ErrorExit;
  1381. }
  1382. if ( (dwStringLength * sizeof (WCHAR)) > MAXUSHORT ) {
  1383. hr = HRESULT_FROM_WIN32( ERROR_BAD_LENGTH );
  1384. LOGERROR( hr, ExpandEnvironmentStringsW );
  1385. goto ErrorExit;
  1386. }
  1387. hr = StringAllocate( pucsNewString, (USHORT)( dwStringLength * sizeof (WCHAR) ));
  1388. if ( FAILED( hr )) {
  1389. LOGERROR( hr, StringAllocate );
  1390. goto ErrorExit;
  1391. }
  1392. //
  1393. // Note that if the expanded string has gotten bigger since we
  1394. // allocated the buffer then too bad, we may not get all the
  1395. // new translation. Not that we really expect these expanded
  1396. // strings to have changed any time recently.
  1397. //
  1398. dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString,
  1399. pucsNewString->Buffer,
  1400. pucsNewString->MaximumLength / sizeof (WCHAR));
  1401. hr = GET_HR_FROM_BOOL( dwStringLength != 0 );
  1402. if ( FAILED ( hr )) {
  1403. LOGERROR( hr, ExpandEnvironmentStringsW );
  1404. goto ErrorExit;
  1405. }
  1406. pucsNewString->Length = (USHORT) ((dwStringLength - 1) * sizeof (WCHAR));
  1407. goto ret;
  1408. ErrorExit:
  1409. CL_ASSERT( FAILED( hr ));
  1410. ret:
  1411. CL_ASSERT( pucsNewString->Length <= pucsNewString->MaximumLength );
  1412. return hr;
  1413. }
  1414. static HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString,
  1415. PUNICODE_STRING pucsOriginalString,
  1416. DWORD dwExtraChars)
  1417. {
  1418. HRESULT hr = NOERROR;
  1419. ULONG ulStringLength = pucsOriginalString->MaximumLength + (dwExtraChars * sizeof (WCHAR));
  1420. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL))) {
  1421. hr = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  1422. goto ErrorExit;
  1423. }
  1424. hr = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL)));
  1425. if ( FAILED( hr ))
  1426. goto ErrorExit;
  1427. memcpy (pucsNewString->Buffer, pucsOriginalString->Buffer, pucsOriginalString->Length);
  1428. pucsNewString->Length = pucsOriginalString->Length;
  1429. pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL;
  1430. goto ret;
  1431. ErrorExit:
  1432. CL_ASSERT( FAILED( hr ));
  1433. ret:
  1434. return hr;
  1435. }
  1436. static void StringAppendString( PUNICODE_STRING pucsTarget, PUNICODE_STRING pucsSource )
  1437. {
  1438. CL_ASSERT( pucsTarget->Length <= pucsTarget->MaximumLength );
  1439. CL_ASSERT( pucsSource->Length <= pucsSource->MaximumLength );
  1440. CL_ASSERT( pucsTarget->Length + pucsSource->Length < pucsTarget->MaximumLength );
  1441. memmove( &pucsTarget->Buffer [pucsTarget->Length / sizeof (WCHAR)],
  1442. pucsSource->Buffer,
  1443. pucsSource->Length + sizeof( UNICODE_NULL ));
  1444. pucsTarget->Length = pucsTarget->Length + pucsSource->Length;
  1445. CL_ASSERT( pucsTarget->Length <= pucsTarget->MaximumLength );
  1446. CL_ASSERT( pucsSource->Length <= pucsSource->MaximumLength );
  1447. }
  1448. static void StringAppendString( PUNICODE_STRING pucsTarget, PWCHAR pwszSource )
  1449. {
  1450. CL_ASSERT( pucsTarget->Length <= pucsTarget->MaximumLength );
  1451. CL_ASSERT( pucsTarget->Length + ( wcslen( pwszSource ) * sizeof( WCHAR )) < pucsTarget->MaximumLength );
  1452. USHORT Length = (USHORT) wcslen( pwszSource ) * sizeof ( WCHAR );
  1453. memmove( &pucsTarget->Buffer [pucsTarget->Length / sizeof (WCHAR)], pwszSource, Length + sizeof( UNICODE_NULL ));
  1454. pucsTarget->Length = pucsTarget->Length + Length;
  1455. CL_ASSERT( pucsTarget->Length <= pucsTarget->MaximumLength );
  1456. }
  1457. static HRESULT StringTruncate (PUNICODE_STRING pucsString, USHORT usSizeInChars)
  1458. {
  1459. HRESULT hr = NOERROR;
  1460. USHORT usNewLength = (USHORT)(usSizeInChars * sizeof (WCHAR));
  1461. if (usNewLength > pucsString->Length) {
  1462. hr = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  1463. } else {
  1464. pucsString->Buffer [usSizeInChars] = UNICODE_NULL;
  1465. pucsString->Length = usNewLength;
  1466. }
  1467. return hr;
  1468. }
  1469. #pragma warning( pop )