Source code of Windows XP (NT5)
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.

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