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.

2665 lines
76 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Abstract:
  4. module common.cxx | Implementation of SnapshotWriter common code
  5. Author:
  6. Michael C. Johnson [mikejohn] 03-Feb-2000
  7. Stefan R. Steiner [SSteiner] 27-Jul-2001
  8. Description:
  9. Contains general code and definitions used by the Shim writer and the various other writers.
  10. Revision History:
  11. reuvenl 05/02/2002 Incorporated code from common.h/cpp into this module
  12. --*/
  13. #include "stdafx.h"
  14. #include "vssmsg.h"
  15. #include "wrtcommon.hxx"
  16. #include <accctrl.h>
  17. #include <aclapi.h>
  18. ////////////////////////////////////////////////////////////////////////
  19. // Standard foo for file name aliasing. This code block must be after
  20. // all includes of VSS header files.
  21. //
  22. #ifdef VSS_FILE_ALIAS
  23. #undef VSS_FILE_ALIAS
  24. #endif
  25. #define VSS_FILE_ALIAS "WSHCOMNC"
  26. // Definitions for internal static functions
  27. static HRESULT ConstructSecurityAttributes (
  28. IN OUT PSECURITY_ATTRIBUTES psaSecurityAttributes,
  29. IN BOOL bIncludeBackupOperator
  30. );
  31. static VOID CleanupSecurityAttributes(
  32. IN PSECURITY_ATTRIBUTES psaSecurityAttributes
  33. );
  34. static BOOL FixupFileInfo(
  35. IN LPCWSTR pwszPathName,
  36. IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  37. IN DWORD dwExtraAttributes);
  38. static PWCHAR const GetStringFromStateCode (DWORD dwState);
  39. static DWORD const GetControlCodeFromTargetState (const DWORD dwTargetState);
  40. static DWORD const GetNormalisedState (DWORD dwCurrentState);
  41. static HRESULT WaitForServiceToEnterState (SC_HANDLE shService,
  42. DWORD dwMaxDelayInMilliSeconds,
  43. const DWORD dwDesiredState);
  44. /*
  45. ** The first group of (overloaded) routines manipulate UNICODE_STRING
  46. ** strings. The rules which apply here are:
  47. **
  48. ** 1) The Buffer field points to an array of characters (WCHAR) with
  49. ** the length of the buffer being specified in the MaximumLength
  50. ** field. If the Buffer field is non-NULL it must point to a valid
  51. ** buffer capable of holding at least one character. If the Buffer
  52. ** field is NULL, the MaximumLength and Length fields must both be
  53. ** zero.
  54. **
  55. ** 2) Any valid string in the buffer is always terminated with a
  56. ** UNICODE_NULL.
  57. **
  58. ** 3) the MaximumLength describes the length of the buffer measured in
  59. ** bytes. This value must be even.
  60. **
  61. ** 4) The Length field describes the number of valid characters in the
  62. ** buffer measured in BYTES, excluding the termination
  63. ** character. Since the string must always have a termination
  64. ** character ('\0'), the maximum value of Length is MaximumLength - 2.
  65. **
  66. **
  67. ** The routines available are:-
  68. **
  69. ** StringInitialise ()
  70. ** StringTruncate ()
  71. ** StringSetLength ()
  72. ** StringAllocate ()
  73. ** StringFree ()
  74. ** StringCreateFromString ()
  75. ** StringAppendString ()
  76. ** StringCreateFromExpandedString ()
  77. **
  78. */
  79. /*
  80. **++
  81. **
  82. ** Routine Description:
  83. **
  84. **
  85. ** Arguments:
  86. **
  87. **
  88. ** Side Effects:
  89. **
  90. **
  91. ** Return Value:
  92. **
  93. ** Any HRESULT
  94. **
  95. **--
  96. */
  97. HRESULT StringInitialise (PUNICODE_STRING pucsString)
  98. {
  99. pucsString->Buffer = NULL;
  100. pucsString->Length = 0;
  101. pucsString->MaximumLength = 0;
  102. return (NOERROR);
  103. } /* StringInitialise () */
  104. HRESULT StringInitialise (PUNICODE_STRING pucsString, LPCWSTR pwszString)
  105. {
  106. return (StringInitialise (pucsString, (PWCHAR) pwszString));
  107. }
  108. HRESULT StringInitialise (PUNICODE_STRING pucsString, PWCHAR pwszString)
  109. {
  110. HRESULT hrStatus = NOERROR;
  111. ULONG ulStringLength = wcslen (pwszString) * sizeof (WCHAR);
  112. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL)))
  113. {
  114. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  115. }
  116. else
  117. {
  118. pucsString->Buffer = pwszString;
  119. pucsString->Length = (USHORT) ulStringLength;
  120. pucsString->MaximumLength = (USHORT) (ulStringLength + sizeof (UNICODE_NULL));
  121. }
  122. return (hrStatus);
  123. } /* StringInitialise () */
  124. HRESULT StringTruncate (PUNICODE_STRING pucsString, USHORT usSizeInChars)
  125. {
  126. HRESULT hrStatus = NOERROR;
  127. USHORT usNewLength = (USHORT)(usSizeInChars * sizeof (WCHAR));
  128. if (usNewLength > pucsString->Length)
  129. {
  130. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  131. }
  132. else
  133. {
  134. pucsString->Buffer [usSizeInChars] = UNICODE_NULL;
  135. pucsString->Length = usNewLength;
  136. }
  137. return (hrStatus);
  138. } /* StringTruncate () */
  139. HRESULT StringSetLength (PUNICODE_STRING pucsString)
  140. {
  141. HRESULT hrStatus = NOERROR;
  142. ULONG ulStringLength = wcslen (pucsString->Buffer) * sizeof (WCHAR);
  143. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL)))
  144. {
  145. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  146. }
  147. else
  148. {
  149. pucsString->Length = (USHORT) ulStringLength;
  150. pucsString->MaximumLength = (USHORT) UMAX (pucsString->MaximumLength,
  151. pucsString->Length + sizeof (UNICODE_NULL));
  152. }
  153. return (hrStatus);
  154. } /* StringSetLength () */
  155. HRESULT StringAllocate (PUNICODE_STRING pucsString, USHORT usMaximumStringLengthInBytes)
  156. {
  157. HRESULT hrStatus = NOERROR;
  158. LPVOID pvBuffer = NULL;
  159. SIZE_T cActualLength = 0;
  160. pvBuffer = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, usMaximumStringLengthInBytes);
  161. hrStatus = GET_STATUS_FROM_POINTER (pvBuffer);
  162. if (SUCCEEDED (hrStatus))
  163. {
  164. pucsString->Buffer = (PWCHAR)pvBuffer;
  165. pucsString->Length = 0;
  166. pucsString->MaximumLength = usMaximumStringLengthInBytes;
  167. cActualLength = HeapSize (GetProcessHeap (), 0, pvBuffer);
  168. if ((cActualLength <= MAXUSHORT) && (cActualLength > usMaximumStringLengthInBytes))
  169. {
  170. pucsString->MaximumLength = (USHORT) cActualLength;
  171. }
  172. }
  173. return (hrStatus);
  174. } /* StringAllocate () */
  175. HRESULT StringFree (PUNICODE_STRING pucsString)
  176. {
  177. HRESULT hrStatus = NOERROR;
  178. BOOL bSucceeded;
  179. if (NULL != pucsString->Buffer)
  180. {
  181. bSucceeded = HeapFree (GetProcessHeap (), 0, pucsString->Buffer);
  182. hrStatus = GET_STATUS_FROM_BOOL (bSucceeded);
  183. }
  184. if (SUCCEEDED (hrStatus))
  185. {
  186. pucsString->Buffer = NULL;
  187. pucsString->Length = 0;
  188. pucsString->MaximumLength = 0;
  189. }
  190. return (hrStatus);
  191. } /* StringFree () */
  192. HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString)
  193. {
  194. HRESULT hrStatus = NOERROR;
  195. hrStatus = StringAllocate (pucsNewString, pucsOriginalString->MaximumLength);
  196. if (SUCCEEDED (hrStatus))
  197. {
  198. memcpy (pucsNewString->Buffer, pucsOriginalString->Buffer, pucsOriginalString->Length);
  199. pucsNewString->Length = pucsOriginalString->Length;
  200. pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL;
  201. }
  202. return (hrStatus);
  203. } /* StringCreateFromString () */
  204. HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString)
  205. {
  206. HRESULT hrStatus = NOERROR;
  207. ULONG ulStringLength = wcslen (pwszOriginalString) * sizeof (WCHAR);
  208. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL)))
  209. {
  210. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  211. }
  212. if (SUCCEEDED (hrStatus))
  213. {
  214. hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL)));
  215. }
  216. if (SUCCEEDED (hrStatus))
  217. {
  218. memcpy (pucsNewString->Buffer, pwszOriginalString, ulStringLength);
  219. pucsNewString->Length = (USHORT) ulStringLength;
  220. pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL;
  221. }
  222. return (hrStatus);
  223. } /* StringCreateFromString () */
  224. HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString, DWORD dwExtraChars)
  225. {
  226. HRESULT hrStatus = NOERROR;
  227. ULONG ulStringLength = pucsOriginalString->MaximumLength + (dwExtraChars * sizeof (WCHAR));
  228. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL)))
  229. {
  230. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  231. }
  232. if (SUCCEEDED (hrStatus))
  233. {
  234. hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL)));
  235. }
  236. if (SUCCEEDED (hrStatus))
  237. {
  238. memcpy (pucsNewString->Buffer, pucsOriginalString->Buffer, pucsOriginalString->Length);
  239. pucsNewString->Length = pucsOriginalString->Length;
  240. pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL;
  241. }
  242. return (hrStatus);
  243. } /* StringCreateFromString () */
  244. HRESULT StringCreateFromString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars)
  245. {
  246. HRESULT hrStatus = NOERROR;
  247. ULONG ulStringLength = (wcslen (pwszOriginalString) + dwExtraChars) * sizeof (WCHAR);
  248. if (ulStringLength >= (MAXUSHORT - sizeof (UNICODE_NULL)))
  249. {
  250. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  251. }
  252. if (SUCCEEDED (hrStatus))
  253. {
  254. hrStatus = StringAllocate (pucsNewString, (USHORT) (ulStringLength + sizeof (UNICODE_NULL)));
  255. }
  256. if (SUCCEEDED (hrStatus))
  257. {
  258. memcpy (pucsNewString->Buffer, pwszOriginalString, wcslen(pwszOriginalString)*sizeof(WCHAR));
  259. pucsNewString->Length = (USHORT) (wcslen(pwszOriginalString)*sizeof(WCHAR));
  260. pucsNewString->Buffer [pucsNewString->Length / sizeof (WCHAR)] = UNICODE_NULL;
  261. }
  262. return (hrStatus);
  263. } /* StringCreateFromString () */
  264. HRESULT StringAppendString (PUNICODE_STRING pucsTarget, PUNICODE_STRING pucsSource)
  265. {
  266. HRESULT hrStatus = NOERROR;
  267. if (pucsSource->Length > (pucsTarget->MaximumLength - pucsTarget->Length - sizeof (UNICODE_NULL)))
  268. {
  269. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  270. }
  271. else
  272. {
  273. memmove (&pucsTarget->Buffer [pucsTarget->Length / sizeof (WCHAR)],
  274. pucsSource->Buffer,
  275. pucsSource->Length + sizeof (UNICODE_NULL));
  276. pucsTarget->Length += pucsSource->Length;
  277. }
  278. /*
  279. ** There should be no reason in this code using this routine to
  280. ** have to deal with a short buffer so trap potential problems.
  281. */
  282. BS_ASSERT (SUCCEEDED (hrStatus));
  283. return (hrStatus);
  284. } /* StringAppendString () */
  285. HRESULT StringAppendString (PUNICODE_STRING pucsTarget, PWCHAR pwszSource)
  286. {
  287. HRESULT hrStatus = NOERROR;
  288. UNICODE_STRING ucsSource;
  289. StringInitialise (&ucsSource, pwszSource);
  290. hrStatus = StringAppendString (pucsTarget, &ucsSource);
  291. /*
  292. ** There should be no reason in this code using this routine to
  293. ** have to deal with a short buffer so trap potential problems.
  294. */
  295. BS_ASSERT (SUCCEEDED (hrStatus));
  296. return (hrStatus);
  297. } /* StringAppendString () */
  298. HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString)
  299. {
  300. return (StringCreateFromExpandedString (pucsNewString, pwszOriginalString, 0));
  301. }
  302. HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString)
  303. {
  304. return (StringCreateFromExpandedString (pucsNewString, pucsOriginalString->Buffer, 0));
  305. }
  306. HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, PUNICODE_STRING pucsOriginalString, DWORD dwExtraChars)
  307. {
  308. return (StringCreateFromExpandedString (pucsNewString, pucsOriginalString->Buffer, dwExtraChars));
  309. }
  310. HRESULT StringCreateFromExpandedString (PUNICODE_STRING pucsNewString, LPCWSTR pwszOriginalString, DWORD dwExtraChars)
  311. {
  312. HRESULT hrStatus = NOERROR;
  313. DWORD dwStringLength;
  314. /*
  315. ** Remember, ExpandEnvironmentStringsW () includes the terminating null in the response.
  316. */
  317. dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString, NULL, 0) + dwExtraChars;
  318. hrStatus = GET_STATUS_FROM_BOOL (0 != dwStringLength);
  319. if (SUCCEEDED (hrStatus) && ((dwStringLength * sizeof (WCHAR)) > MAXUSHORT))
  320. {
  321. hrStatus = HRESULT_FROM_WIN32 (ERROR_BAD_LENGTH);
  322. }
  323. if (SUCCEEDED (hrStatus))
  324. {
  325. hrStatus = StringAllocate (pucsNewString, (USHORT)(dwStringLength * sizeof (WCHAR)));
  326. }
  327. if (SUCCEEDED (hrStatus))
  328. {
  329. /*
  330. ** Note that if the expanded string has gotten bigger since we
  331. ** allocated the buffer then too bad, we may not get all the
  332. ** new translation. Not that we really expect these expanded
  333. ** strings to have changed any time recently.
  334. */
  335. dwStringLength = ExpandEnvironmentStringsW (pwszOriginalString,
  336. pucsNewString->Buffer,
  337. pucsNewString->MaximumLength / sizeof (WCHAR));
  338. hrStatus = GET_STATUS_FROM_BOOL (0 != dwStringLength);
  339. if (SUCCEEDED (hrStatus))
  340. {
  341. pucsNewString->Length = (USHORT) ((dwStringLength - 1) * sizeof (WCHAR));
  342. }
  343. }
  344. return (hrStatus);
  345. } /* StringCreateFromExpandedString () */
  346. /*
  347. **++
  348. **
  349. ** Routine Description:
  350. **
  351. ** Closes a standard Win32 handle and set it to INVALID_HANDLE_VALUE.
  352. ** Safe to be called multiple times on the same handle or on a handle
  353. ** initialised to INVALID_HANDLE_VALUE or NULL.
  354. **
  355. **
  356. ** Arguments:
  357. **
  358. ** phHandle Address of the handle to be closed
  359. **
  360. **
  361. ** Side Effects:
  362. **
  363. **
  364. ** Return Value:
  365. **
  366. ** Any HRESULT from CloseHandle()
  367. **
  368. **--
  369. */
  370. HRESULT CommonCloseHandle (PHANDLE phHandle)
  371. {
  372. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CommonCloseHandle");
  373. HRESULT hrStatus = NOERROR;
  374. BOOL bSucceeded;
  375. if ((INVALID_HANDLE_VALUE != *phHandle) && (NULL != *phHandle))
  376. {
  377. bSucceeded = CloseHandle (*phHandle);
  378. hrStatus = GET_STATUS_FROM_BOOL (bSucceeded);
  379. if (SUCCEEDED (hrStatus))
  380. {
  381. *phHandle = INVALID_HANDLE_VALUE;
  382. }
  383. }
  384. return (hrStatus);
  385. } /* CommonCloseHandle () */
  386. #define VALID_PATH( path ) ( ( (wcslen(path) >= 2) && ( path[0] == DIR_SEP_CHAR ) && ( path[1] == DIR_SEP_CHAR ) ) || \
  387. ( (wcslen(path) >= 3) && iswalpha( path[0] ) && ( path[1] == L':' ) && ( path[2] == DIR_SEP_CHAR ) ) )
  388. /*++
  389. **
  390. ** Routine Description:
  391. **
  392. ** Creates any number of directories along a path. Only works for
  393. ** full path names with no relative elements in it. Other than that
  394. ** it works identically as CreateDirectory() works and sets the same
  395. ** error codes except it doesn't return an error if the complete
  396. ** path already exists.
  397. **
  398. ** Arguments:
  399. **
  400. ** pwszPathName - The path with possible directory components to create.
  401. **
  402. ** lpSecurityAttributes -
  403. **
  404. ** Return Value:
  405. **
  406. ** TRUE - Sucessful
  407. ** FALSE - GetLastError() can return one of these (and others):
  408. ** ERROR_ALREADY_EXISTS - when something other than a file exists somewhere in the path.
  409. ** ERROR_BAD_PATHNAME - when \\servername alone is specified in the pathname
  410. ** ERROR_ACCESS_DENIED - when x:\ alone is specified in the pathname and x: exists
  411. ** ERROR_PATH_NOT_FOUND - when x: alone is specified in the pathname and x: doesn't exist.
  412. ** Should not get this error code for any other reason.
  413. ** ERROR_INVALID_NAME - when pathname doesn't start with either x:\ or \\
  414. **
  415. **--
  416. */
  417. BOOL VsCreateDirectories (
  418. IN LPCWSTR pwszPathName,
  419. IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  420. IN DWORD dwExtraAttributes)
  421. {
  422. DWORD dwObjAttribs, dwRetPreserve;
  423. BOOL bRet;
  424. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"VsCreateDirectories");
  425. /*
  426. ** Make sure the path starts with the valid path prefixes
  427. */
  428. if (!VALID_PATH (pwszPathName))
  429. {
  430. SetLastError (ERROR_INVALID_NAME);
  431. return FALSE;
  432. }
  433. /*
  434. ** Save away the current last error code.
  435. */
  436. dwRetPreserve = GetLastError ();
  437. /*
  438. ** Now test for the most common case, the directory already exists. This
  439. ** is the performance path.
  440. */
  441. dwObjAttribs = GetFileAttributesW (pwszPathName);
  442. if ((dwObjAttribs != 0xFFFFFFFF) && (dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY))
  443. {
  444. /*
  445. ** Don't return an error if the directory already exists.
  446. ** This is the one case where this function differs from
  447. ** CreateDirectory(). Notice that even though another type of
  448. ** file may exist with this pathname, no error is returned yet
  449. ** since I want the error to come from CreateDirectory() to
  450. ** get CreateDirectory() error behavior.
  451. **
  452. ** Since we're successful restore the last error code.
  453. */
  454. if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == TRUE)
  455. {
  456. SetLastError (dwRetPreserve);
  457. return TRUE;
  458. }
  459. return FALSE;
  460. }
  461. /*
  462. ** Now try to create the directory using the full path. Even
  463. ** though we might already know it exists as something other than
  464. ** a directory, get the error from CreateDirectory() instead of
  465. ** having to try to reverse engineer all possible errors that
  466. ** CreateDirectory() can return in the above code.
  467. **
  468. ** It is probably the second most common case that when this
  469. ** function is called that only the last component in the
  470. ** directory doesn't exist. Let's try to make it.
  471. **
  472. ** Note that it appears if a UNC path is given with a number of
  473. ** non-existing path components the remote server automatically
  474. ** creates all of those components when CreateDirectory is called.
  475. ** Therefore, the next call is almost always successful when the
  476. ** path is a UNC.
  477. */
  478. bRet = CreateDirectoryW (pwszPathName, lpSecurityAttributes);
  479. if (bRet)
  480. {
  481. SetFileAttributesW (pwszPathName, dwExtraAttributes);
  482. /*
  483. ** Set it back to the last error code
  484. */
  485. SetLastError (dwRetPreserve);
  486. return TRUE;
  487. }
  488. else if (GetLastError () == ERROR_ALREADY_EXISTS)
  489. {
  490. /*
  491. ** Looks like someone created the name while we weren't
  492. ** looking. Check to see if it's a directory and return
  493. ** success if so, otherwise return the error that
  494. ** CreateDirectoryW() set.
  495. */
  496. dwObjAttribs = GetFileAttributesW (pwszPathName);
  497. if ((dwObjAttribs != 0xFFFFFFFF) && (dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY))
  498. {
  499. /*
  500. ** It's a directory. Declare victory.
  501. **
  502. ** Restore the last error code
  503. */
  504. if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == TRUE)
  505. {
  506. SetLastError (dwRetPreserve);
  507. return TRUE;
  508. }
  509. return FALSE;
  510. }
  511. else
  512. {
  513. SetLastError (ERROR_ALREADY_EXISTS);
  514. return FALSE;
  515. }
  516. }
  517. else if (GetLastError () != ERROR_PATH_NOT_FOUND )
  518. {
  519. return FALSE;
  520. }
  521. /*
  522. ** Allocate memory to hold the string while processing the path.
  523. ** The passed in string is a const.
  524. */
  525. PWCHAR pwszTempPath = (PWCHAR) malloc ((wcslen (pwszPathName) + 1) * sizeof (WCHAR));
  526. BS_ASSERT (pwszTempPath != NULL);
  527. wcscpy (pwszTempPath, pwszPathName);
  528. /*
  529. ** Appears some components in the path don't exist. Now try to
  530. ** create the components.
  531. */
  532. PWCHAR pwsz, pwszSlash;
  533. /*
  534. ** First skip the drive letter part or the \\server\sharename
  535. ** part and get to the first slash before the first level
  536. ** directory component.
  537. */
  538. if (pwszTempPath [1] == L':')
  539. {
  540. /*
  541. ** Path in the form of x:\..., skip first 2 chars
  542. */
  543. pwsz = pwszTempPath + 2;
  544. }
  545. else
  546. {
  547. /*
  548. ** Path should be in form of \\servername\sharename. Can be
  549. ** \\?\d: Search to first slash after sharename
  550. **
  551. ** First search to first char of the share name
  552. */
  553. pwsz = pwszTempPath + 2;
  554. while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR))
  555. {
  556. ++pwsz;
  557. }
  558. /*
  559. ** Eat up all continuous slashes and get to first char of the
  560. ** share name
  561. */
  562. while (*pwsz == DIR_SEP_CHAR)
  563. {
  564. ++pwsz;
  565. }
  566. if (*pwsz == L'\0')
  567. {
  568. /*
  569. ** This shouldn't have happened since the CreateDirectory
  570. ** call should have caught it. Oh, well, deal with it.
  571. */
  572. SetLastError (ERROR_BAD_PATHNAME);
  573. free (pwszTempPath);
  574. return FALSE;
  575. }
  576. /*
  577. ** Now at first char of share name, let's search for first
  578. ** slash after the share name to get to the (first) shash in
  579. ** front the first level directory.
  580. */
  581. while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR))
  582. {
  583. ++pwsz;
  584. }
  585. }
  586. /*
  587. ** Eat up all continuous slashes before the first level directory
  588. */
  589. while (*pwsz == DIR_SEP_CHAR)
  590. {
  591. ++pwsz;
  592. }
  593. /*
  594. ** Now at first char of the first level directory, let's search
  595. ** for first slash after the directory.
  596. */
  597. while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR))
  598. {
  599. ++pwsz;
  600. }
  601. /*
  602. ** If pwsz is pointing to a null char, that means only the first
  603. ** level directory needs to be created. Fall through to the leaf
  604. ** node create directory.
  605. */
  606. while (*pwsz != L'\0')
  607. {
  608. pwszSlash = pwsz; // Keep pointer to the separator
  609. /*
  610. ** Eat up all continuous slashes.
  611. */
  612. while (*pwsz == DIR_SEP_CHAR)
  613. {
  614. ++pwsz;
  615. }
  616. if (*pwsz == L'\0')
  617. {
  618. /*
  619. ** There were just slashes at the end of the path. Break
  620. ** out of loop, let the leaf node CreateDirectory create
  621. ** the last directory.
  622. */
  623. break;
  624. }
  625. /*
  626. ** Terminate the directory path at the current level.
  627. */
  628. *pwszSlash = L'\0';
  629. dwObjAttribs = GetFileAttributesW (pwszTempPath);
  630. if ((dwObjAttribs == 0XFFFFFFFF) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0))
  631. {
  632. bRet = CreateDirectoryW (pwszTempPath, lpSecurityAttributes);
  633. if (bRet)
  634. {
  635. SetFileAttributesW (pwszTempPath, dwExtraAttributes);
  636. }
  637. else
  638. {
  639. if (ERROR_ALREADY_EXISTS != GetLastError ())
  640. {
  641. /*
  642. ** Restore the slash.
  643. */
  644. *pwszSlash = DIR_SEP_CHAR;
  645. free (pwszTempPath);
  646. return FALSE;
  647. }
  648. else
  649. {
  650. /*
  651. ** Looks like someone created the name whilst we
  652. ** weren't looking. Check to see if it's a
  653. ** directory and continue if so, otherwise return
  654. ** the error that CreateDirectoryW() set.
  655. */
  656. dwObjAttribs = GetFileAttributesW (pwszTempPath);
  657. if ((dwObjAttribs == 0xFFFFFFFF) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0))
  658. {
  659. /*
  660. ** It's not what we recognise as a
  661. ** directory. Declare failure. Set the error
  662. ** code to that which CreateDirectoryW()
  663. ** returned, restore the slash, free the
  664. ** buffer and get out of here.
  665. */
  666. SetLastError (ERROR_ALREADY_EXISTS);
  667. *pwszSlash = DIR_SEP_CHAR;
  668. free (pwszTempPath);
  669. return FALSE;
  670. }
  671. /*
  672. ** If someone deletes the directory right before FixupFileInfo is called, the function will fail, and we'll return a
  673. ** partially-constructed directory tree. This is not expected to happen (and the way we use these functions, only an
  674. ** Administrator can do this), so it's not worth the effort to retry on failure */
  675. if (FixupFileInfo(pwszTempPath, lpSecurityAttributes, dwExtraAttributes) == FALSE)
  676. {
  677. free(pwszTempPath);
  678. return FALSE;
  679. }
  680. }
  681. }
  682. }
  683. /*
  684. ** Restore the slash.
  685. */
  686. *pwszSlash = DIR_SEP_CHAR;
  687. /*
  688. ** Now at first char of the next level directory, let's search
  689. ** for first slash after the directory.
  690. */
  691. while ((*pwsz != L'\0') && (*pwsz != DIR_SEP_CHAR))
  692. {
  693. ++pwsz;
  694. }
  695. }
  696. free (pwszTempPath);
  697. pwszTempPath = NULL;
  698. /*
  699. ** Now make the last directory.
  700. */
  701. dwObjAttribs = GetFileAttributesW (pwszPathName);
  702. if ((dwObjAttribs == 0xFFFFffff) || ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) == 0))
  703. {
  704. bRet = CreateDirectoryW (pwszPathName, lpSecurityAttributes);
  705. if (bRet)
  706. {
  707. SetFileAttributesW (pwszPathName, dwExtraAttributes);
  708. }
  709. else
  710. {
  711. // someone sneakily created the directory here
  712. dwObjAttribs = GetFileAttributesW (pwszPathName);
  713. if ((dwObjAttribs != 0xFFFFFFFF) && ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0))
  714. {
  715. if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == FALSE)
  716. return FALSE;
  717. }
  718. else
  719. return FALSE;
  720. }
  721. }
  722. else if ( (dwObjAttribs != 0xFFFFFFFF) && ((dwObjAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0)) // or here
  723. {
  724. if (FixupFileInfo(pwszPathName, lpSecurityAttributes, dwExtraAttributes) == FALSE)
  725. return FALSE;
  726. }
  727. SetLastError (dwRetPreserve); // Set back old last error code
  728. return TRUE;
  729. }
  730. /*
  731. **++
  732. **
  733. ** Routine Description:
  734. **
  735. ** Changes the attributes and security information
  736. ** for a file or a directory.
  737. **
  738. ** Arguments:
  739. **
  740. ** pwszPathName The directory path to change
  741. ** lpSecurityAttributes The new security information for the directory
  742. ** dwExtraAttributes The new attributes for the directory
  743. **
  744. **
  745. ** Side Effects:
  746. **
  747. ** None
  748. **
  749. **
  750. ** Return Value:
  751. **
  752. ** TRUE -- Success
  753. FALSE -- Failure. GetLastError will contain the appropriate error code except
  754. ** for the case where we tried this on something that isn't a directory
  755. **
  756. **--
  757. */
  758. BOOL FixupFileInfo(
  759. IN LPCWSTR pwszPathName,
  760. IN LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  761. IN DWORD dwExtraAttributes)
  762. {
  763. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"FixupFileInfo");
  764. // try and push down the required attributes
  765. if (SetFileAttributes(pwszPathName, dwExtraAttributes) == FALSE)
  766. {
  767. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  768. return FALSE;
  769. }
  770. // gather security information from the security descriptor
  771. PSID pOwner = NULL;
  772. PSID pGroup = NULL;
  773. PACL pDacl = NULL;
  774. PACL pSacl = NULL;
  775. BOOL ownerDefaulted, groupDefaulted, DaclDefaulted, SaclDefaulted, DaclPresent, SaclPresent;
  776. SECURITY_DESCRIPTOR_CONTROL control;
  777. DWORD revision = 0;
  778. if ((GetSecurityDescriptorOwner(lpSecurityAttributes->lpSecurityDescriptor, &pOwner, &ownerDefaulted) == FALSE) ||
  779. (GetSecurityDescriptorGroup(lpSecurityAttributes->lpSecurityDescriptor, &pGroup, &groupDefaulted) == FALSE) ||
  780. (GetSecurityDescriptorDacl(lpSecurityAttributes->lpSecurityDescriptor, &DaclPresent, &pDacl, &DaclDefaulted) == FALSE) ||
  781. (GetSecurityDescriptorSacl(lpSecurityAttributes->lpSecurityDescriptor, &SaclPresent, &pSacl, &SaclDefaulted) == FALSE) ||
  782. (GetSecurityDescriptorControl(lpSecurityAttributes->lpSecurityDescriptor, &control, &revision) == FALSE))
  783. {
  784. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  785. return FALSE;
  786. }
  787. SECURITY_INFORMATION securityInformation;
  788. securityInformation = ((pDacl != NULL) ? DACL_SECURITY_INFORMATION : 0) |
  789. ((pDacl != NULL && ((control & SE_DACL_PROTECTED) != 0))
  790. ? PROTECTED_DACL_SECURITY_INFORMATION : 0) |
  791. ((pSacl != NULL) ? SACL_SECURITY_INFORMATION : 0) |
  792. ((pSacl != NULL && ((control & SE_SACL_PROTECTED) != 0))
  793. ? PROTECTED_SACL_SECURITY_INFORMATION : 0) |
  794. ((pOwner != NULL) ? OWNER_SECURITY_INFORMATION : 0) |
  795. ((pGroup != NULL) ? GROUP_SECURITY_INFORMATION : 0);
  796. // push down the new security information to the file
  797. ft.hr = SetNamedSecurityInfo(const_cast<LPWSTR>(pwszPathName),
  798. SE_FILE_OBJECT, securityInformation, pOwner, pGroup, pDacl, pSacl);
  799. return ft.HrSucceeded();
  800. }
  801. /*
  802. ** The next set of rountes are used to change the state of SCM
  803. ** controlled services, typically between RUNNING and either PAUSED or
  804. ** STOPPED.
  805. **
  806. ** The initial collection are for manipulating the states, control
  807. ** codes and getting the string equivalents to be used for tracing
  808. ** purposes.
  809. **
  810. ** The major routines is VsServiceChangeState(). This is called
  811. ** specifying the reuiqred state for the service and after some
  812. ** validation, it makes the appropriate request of the SCM and calls
  813. ** WaitForServiceToEnterState() to wait until the services reaches the
  814. ** desired state, or it times out.
  815. */
  816. static PWCHAR const GetStringFromStateCode (DWORD dwState)
  817. {
  818. PWCHAR pwszReturnedString = NULL;
  819. switch (dwState)
  820. {
  821. case 0: pwszReturnedString = L"UnSpecified"; break;
  822. case SERVICE_STOPPED: pwszReturnedString = L"Stopped"; break;
  823. case SERVICE_START_PENDING: pwszReturnedString = L"StartPending"; break;
  824. case SERVICE_STOP_PENDING: pwszReturnedString = L"StopPending"; break;
  825. case SERVICE_RUNNING: pwszReturnedString = L"Running"; break;
  826. case SERVICE_CONTINUE_PENDING: pwszReturnedString = L"ContinuePending"; break;
  827. case SERVICE_PAUSE_PENDING: pwszReturnedString = L"PausePending"; break;
  828. case SERVICE_PAUSED: pwszReturnedString = L"Paused"; break;
  829. default: pwszReturnedString = L"UNKKNOWN STATE"; break;
  830. }
  831. return (pwszReturnedString);
  832. } /* GetStringFromStateCode () */
  833. static DWORD const GetControlCodeFromTargetState (const DWORD dwTargetState)
  834. {
  835. DWORD dwServiceControlCode;
  836. switch (dwTargetState)
  837. {
  838. case SERVICE_STOPPED: dwServiceControlCode = SERVICE_CONTROL_STOP; break;
  839. case SERVICE_PAUSED: dwServiceControlCode = SERVICE_CONTROL_PAUSE; break;
  840. case SERVICE_RUNNING: dwServiceControlCode = SERVICE_CONTROL_CONTINUE; break;
  841. default: dwServiceControlCode = 0; break;
  842. }
  843. return (dwServiceControlCode);
  844. } /* GetControlCodeFromTargetState () */
  845. static DWORD const GetNormalisedState (DWORD dwCurrentState)
  846. {
  847. DWORD dwNormalisedState;
  848. switch (dwCurrentState)
  849. {
  850. case SERVICE_STOPPED:
  851. case SERVICE_STOP_PENDING:
  852. dwNormalisedState = SERVICE_STOPPED;
  853. break;
  854. case SERVICE_START_PENDING:
  855. case SERVICE_CONTINUE_PENDING:
  856. case SERVICE_RUNNING:
  857. dwNormalisedState = SERVICE_RUNNING;
  858. break;
  859. case SERVICE_PAUSED:
  860. case SERVICE_PAUSE_PENDING:
  861. dwNormalisedState = SERVICE_PAUSED;
  862. break;
  863. default:
  864. dwNormalisedState = 0;
  865. break;
  866. }
  867. return (dwNormalisedState);
  868. } /* GetNormalisedState () */
  869. /*
  870. **++
  871. **
  872. ** Routine Description:
  873. **
  874. ** Wait for the specified service to enter the specified
  875. ** state. The routine polls the serivce for it's current state
  876. ** every dwServiceStatePollingIntervalInMilliSeconds milliseconds
  877. ** to see if the service has reached the desired state. If the
  878. ** repeated delay eventually reaches the timeout period the
  879. ** routine stops polling and returns a failure status.
  880. **
  881. ** NOTE: since this routine just sleeps between service state
  882. ** interrogations, it effectively stalls from the point of view
  883. ** of the caller.
  884. **
  885. **
  886. ** Arguments:
  887. **
  888. ** shService handle to the service being manipulated
  889. ** dwMaxDelayInMilliSeconds timeout period
  890. ** dwDesiredState state to move the service into
  891. **
  892. **
  893. ** Side Effects:
  894. **
  895. **
  896. ** Return Value:
  897. **
  898. ** HRESULT for ERROR_TIMOUT if the service did not reach the required state in the required time
  899. **
  900. **--
  901. */
  902. static HRESULT WaitForServiceToEnterState (SC_HANDLE shService,
  903. DWORD dwMaxDelayInMilliSeconds,
  904. const DWORD dwDesiredState)
  905. {
  906. CVssFunctionTracer ft (VSSDBG_WRTCMN, L"WaitForServiceToEnterState");
  907. DWORD dwRemainingDelay = dwMaxDelayInMilliSeconds;
  908. DWORD dwInitialState;
  909. const DWORD dwServiceStatePollingIntervalInMilliSeconds = 100;
  910. BOOL bSucceeded;
  911. SERVICE_STATUS sSStat;
  912. try
  913. {
  914. bSucceeded = QueryServiceStatus (shService, &sSStat);
  915. ft.hr = GET_STATUS_FROM_BOOL (bSucceeded);
  916. dwInitialState = sSStat.dwCurrentState;
  917. ft.Trace (VSSDBG_WRTCMN,
  918. L"Initial QueryServiceStatus returned: 0x%08X with current state '%s' and desired state '%s'",
  919. ft.hr,
  920. GetStringFromStateCode (dwInitialState),
  921. GetStringFromStateCode (dwDesiredState));
  922. while ((dwDesiredState != sSStat.dwCurrentState) && (dwRemainingDelay > 0))
  923. {
  924. Sleep (UMIN (dwServiceStatePollingIntervalInMilliSeconds, dwRemainingDelay));
  925. dwRemainingDelay -= (UMIN (dwServiceStatePollingIntervalInMilliSeconds, dwRemainingDelay));
  926. if (0 == dwRemainingDelay)
  927. {
  928. ft.Throw (VSSDBG_WRTCMN,
  929. HRESULT_FROM_WIN32 (ERROR_TIMEOUT),
  930. L"Exceeded maximum delay (%dms)",
  931. dwMaxDelayInMilliSeconds);
  932. }
  933. bSucceeded = QueryServiceStatus (shService, &sSStat);
  934. ft.ThrowIf (!bSucceeded,
  935. VSSDBG_WRTCMN,
  936. GET_STATUS_FROM_BOOL (bSucceeded),
  937. L"QueryServiceStatus shows '%s' as current state",
  938. GetStringFromStateCode (sSStat.dwCurrentState));
  939. }
  940. ft.Trace (VSSDBG_WRTCMN,
  941. L"Service state change from '%s' to '%s' took %u milliseconds",
  942. GetStringFromStateCode (dwInitialState),
  943. GetStringFromStateCode (sSStat.dwCurrentState),
  944. dwMaxDelayInMilliSeconds - dwRemainingDelay);
  945. }
  946. VSS_STANDARD_CATCH (ft);
  947. return (ft.hr);
  948. } /* WaitForServiceToEnterState () */
  949. /*
  950. **++
  951. **
  952. ** Routine Description:
  953. **
  954. ** Changes the state of a service if appropriate.
  955. **
  956. **
  957. ** Arguments:
  958. **
  959. ** pwszServiceName The real service name, i.e. cisvc
  960. ** dwRequestedState the state code for the state we wish to enter
  961. ** pdwReturnedOldState pointer to location to receive current service state.
  962. ** Can be NULL of current state not required
  963. ** pbReturnedStateChanged pointer to location to receive flag indicating if
  964. ** service changed state. Pointer can be NULL if flag
  965. ** value not required.
  966. **
  967. **
  968. ** Return Value:
  969. **
  970. ** Any HRESULT resulting from faiure communication with the
  971. ** SCM (Service Control Manager).
  972. **
  973. **--
  974. */
  975. HRESULT VsServiceChangeState (LPCWSTR pwszServiceName,
  976. DWORD dwRequestedState,
  977. PDWORD pdwReturnedOldState,
  978. PBOOL pbReturnedStateChanged)
  979. {
  980. CVssFunctionTracer ft (VSSDBG_WRTCMN, L"VsServiceChangeState");
  981. SC_HANDLE shSCManager = NULL;
  982. SC_HANDLE shSCService = NULL;
  983. DWORD dwOldState = 0;
  984. BOOL bSucceeded;
  985. SERVICE_STATUS sSStat;
  986. const DWORD dwNormalisedRequestedState = GetNormalisedState (dwRequestedState);
  987. ft.Trace (VSSDBG_WRTCMN,
  988. L"Service '%s' requested to change to state '%s' (normalised to '%s')",
  989. pwszServiceName,
  990. GetStringFromStateCode (dwRequestedState),
  991. GetStringFromStateCode (dwNormalisedRequestedState));
  992. RETURN_VALUE_IF_REQUIRED (pbReturnedStateChanged, FALSE);
  993. try
  994. {
  995. /*
  996. ** Connect to the local service control manager
  997. */
  998. shSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
  999. ft.hr = GET_STATUS_FROM_HANDLE (shSCManager);
  1000. ft.ThrowIf (ft.HrFailed (),
  1001. VSSDBG_WRTCMN,
  1002. ft.hr,
  1003. L"Called OpenSCManager()");
  1004. /*
  1005. ** Get a handle to the service
  1006. */
  1007. shSCService = OpenService (shSCManager, pwszServiceName, SERVICE_ALL_ACCESS);
  1008. ft.hr = GET_STATUS_FROM_HANDLE (shSCService);
  1009. /*
  1010. ** If it's an invalid name or the service doesn't exist then
  1011. ** fail gracefully. For all other failures do the normal
  1012. ** thing. Oh yes, if on the off-chance we should happen to
  1013. ** succeed, carry on.
  1014. */
  1015. if ((HRESULT_FROM_WIN32 (ERROR_INVALID_NAME) == ft.hr) ||
  1016. (HRESULT_FROM_WIN32 (ERROR_SERVICE_DOES_NOT_EXIST) == ft.hr))
  1017. {
  1018. ft.Trace (VSSDBG_WRTCMN, L"'%s' service not found", pwszServiceName);
  1019. }
  1020. else if (ft.HrFailed ())
  1021. {
  1022. /*
  1023. ** See if the service doesn't exist
  1024. */
  1025. ft.Throw (VSSDBG_WRTCMN, E_FAIL, L"ERROR - OpenService() returned: %d", ft.hr);
  1026. }
  1027. else
  1028. {
  1029. /*
  1030. ** Now query the service to see what state it is in at the moment.
  1031. */
  1032. bSucceeded = QueryServiceStatus (shSCService, &sSStat);
  1033. ft.ThrowIf (!bSucceeded,
  1034. VSSDBG_WRTCMN,
  1035. GET_STATUS_FROM_BOOL (bSucceeded),
  1036. L"QueryServiceStatus shows '%s' as current state",
  1037. GetStringFromStateCode (sSStat.dwCurrentState));
  1038. dwOldState = sSStat.dwCurrentState;
  1039. /*
  1040. ** Now we decide what to do.
  1041. ** If we are already in the requested state, we do nothing.
  1042. ** If we are stopped and are requested to pause, we do nothing
  1043. ** otherwise we make the attempt to change state.
  1044. */
  1045. if (dwNormalisedRequestedState == dwOldState)
  1046. {
  1047. /*
  1048. ** We are already in the requested state, so do
  1049. ** nothing. We should even tell folk of that. We're
  1050. ** proud to be doing nothing.
  1051. */
  1052. ft.Trace (VSSDBG_WRTCMN,
  1053. L"'%s' service is already in requested state: doing nothing",
  1054. pwszServiceName);
  1055. RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState);
  1056. }
  1057. else if ((SERVICE_STOPPED == sSStat.dwCurrentState) && (SERVICE_PAUSED == dwNormalisedRequestedState))
  1058. {
  1059. /*
  1060. ** Do nothing. Just log the fact and move on.
  1061. */
  1062. ft.Trace (VSSDBG_WRTCMN,
  1063. L"Asked to PAUSE the '%s' service which is already STOPPED",
  1064. pwszServiceName);
  1065. RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState);
  1066. }
  1067. else
  1068. {
  1069. /*
  1070. ** We want a state which is different from the one
  1071. ** we're in at the moment. Generally this just means
  1072. ** calling ControlService() asking for the new state
  1073. ** except if the service is currently stopped. If
  1074. ** that's so, then we call StartService()
  1075. */
  1076. if (SERVICE_STOPPED == sSStat.dwCurrentState)
  1077. {
  1078. /*
  1079. ** Call StartService to get the ball rolling
  1080. */
  1081. bSucceeded = StartService (shSCService, 0, NULL);
  1082. }
  1083. else
  1084. {
  1085. bSucceeded = ControlService (shSCService,
  1086. GetControlCodeFromTargetState (dwNormalisedRequestedState),
  1087. &sSStat);
  1088. }
  1089. ft.ThrowIf (!bSucceeded,
  1090. VSSDBG_WRTCMN,
  1091. GET_STATUS_FROM_BOOL (bSucceeded),
  1092. (SERVICE_STOPPED == sSStat.dwCurrentState)
  1093. ? L"StartService attempting '%s' to '%s', now at '%s'"
  1094. : L"ControlService attempting '%s' to '%s', now at '%s'",
  1095. GetStringFromStateCode (dwOldState),
  1096. GetStringFromStateCode (dwNormalisedRequestedState),
  1097. GetStringFromStateCode (sSStat.dwCurrentState));
  1098. RETURN_VALUE_IF_REQUIRED (pdwReturnedOldState, dwOldState);
  1099. RETURN_VALUE_IF_REQUIRED (pbReturnedStateChanged, TRUE);
  1100. ft.hr = WaitForServiceToEnterState (shSCService, 15000, dwNormalisedRequestedState);
  1101. if (ft.HrFailed ())
  1102. {
  1103. ft.Throw (VSSDBG_WRTCMN,
  1104. ft.hr,
  1105. L"WaitForServiceToEnterState() failed with 0x%08X",
  1106. ft.hr);
  1107. }
  1108. }
  1109. }
  1110. } VSS_STANDARD_CATCH (ft);
  1111. /*
  1112. ** Now close the service and service control manager handles
  1113. */
  1114. if (NULL != shSCService) CloseServiceHandle (shSCService);
  1115. if (NULL != shSCManager) CloseServiceHandle (shSCManager);
  1116. return (ft.hr);
  1117. } /* VsServiceChangeState () */
  1118. /*
  1119. **++
  1120. **
  1121. ** Routine Description:
  1122. **
  1123. ** Deletes all the sub-directories and files in the specified
  1124. ** directory and then deletes the directory itself.
  1125. **
  1126. ** If the directory does not exist then simply return S_OK.
  1127. **
  1128. ** Arguments:
  1129. **
  1130. ** pucsDirectoryPath The directory path to clear out
  1131. **
  1132. **
  1133. ** Side Effects:
  1134. **
  1135. ** None
  1136. **
  1137. **
  1138. ** Return Value:
  1139. **
  1140. ** Out of memory or any HRESULT from
  1141. **
  1142. ** RemoveDirectory()
  1143. ** DeleteFile()
  1144. ** FindFirstFile()
  1145. **
  1146. **--
  1147. */
  1148. HRESULT RemoveDirectoryTree (
  1149. IN LPCWSTR pwszDirectoryPath
  1150. )
  1151. {
  1152. HANDLE hFileScan = INVALID_HANDLE_VALUE;
  1153. DWORD dwSubDirectoriesEntered = 0;
  1154. INT iCurrentPathCursor = 0;
  1155. BOOL bSucceeded;
  1156. WIN32_FIND_DATAW FileFindData;
  1157. CBsString cwsCurrentPath;
  1158. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"RemoveDirectoryTree");
  1159. try
  1160. {
  1161. ft.Trace( VSSDBG_WRTCMN, L"Recursive delete of the '%s' directory", pwszDirectoryPath );
  1162. cwsCurrentPath = pwszDirectoryPath;
  1163. iCurrentPathCursor = cwsCurrentPath.ReverseFind( DIR_SEP_CHAR ) + 1;
  1164. while ( 1 )
  1165. {
  1166. if ( HandleInvalid ( hFileScan ) )
  1167. {
  1168. /*
  1169. ** No valid scan handle so start a new scan
  1170. */
  1171. ft.Trace( VSSDBG_WRTCMN, L"FindFirstFileW( %s, ... )", cwsCurrentPath.c_str() );
  1172. hFileScan = FindFirstFileW( cwsCurrentPath, &FileFindData);
  1173. if ( ( hFileScan == INVALID_HANDLE_VALUE ) && (
  1174. ( ::GetLastError() == ERROR_FILE_NOT_FOUND )
  1175. || ( ::GetLastError() == ERROR_PATH_NOT_FOUND )
  1176. ) )
  1177. {
  1178. // Directory is empty or does not exists
  1179. ft.hr = S_OK;
  1180. break;
  1181. }
  1182. ft.hr = GET_STATUS_FROM_HANDLE( hFileScan );
  1183. ft.CheckForError(VSSDBG_WRTCMN, L"RemoveDirectoryTree - FindFirstFileW");
  1184. cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor );
  1185. cwsCurrentPath += FileFindData.cFileName;
  1186. }
  1187. else
  1188. {
  1189. /*
  1190. ** Continue with the existing scan
  1191. */
  1192. bSucceeded = FindNextFileW( hFileScan, &FileFindData );
  1193. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1194. if ( HRESULT_FROM_WIN32( ERROR_NO_MORE_FILES ) == ft.hr )
  1195. {
  1196. FindClose( hFileScan );
  1197. hFileScan = INVALID_HANDLE_VALUE;
  1198. if ( dwSubDirectoriesEntered > 0 )
  1199. {
  1200. /*
  1201. ** This is a scan of a sub-directory that is now
  1202. ** complete so delete the sub-directory itself.
  1203. */
  1204. cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor - 1 );
  1205. ft.Trace( VSSDBG_WRTCMN, L"RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() );
  1206. bSucceeded = RemoveDirectoryW( cwsCurrentPath );
  1207. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1208. ft.CheckForError(VSSDBG_WRTCMN, L"RemoveDirectoryTree - RemoveDirectoryW");
  1209. dwSubDirectoriesEntered--;
  1210. }
  1211. if ( 0 == dwSubDirectoriesEntered )
  1212. {
  1213. /*
  1214. ** We are back to where we started except that the
  1215. ** requested directory is now gone. Time to leave.
  1216. */
  1217. ft.hr = S_OK;
  1218. break;
  1219. }
  1220. else
  1221. {
  1222. /*
  1223. ** Move back up one directory level, reset the cursor
  1224. ** and prepare the path buffer to begin a new scan.
  1225. */
  1226. iCurrentPathCursor = cwsCurrentPath.ReverseFind( DIR_SEP_CHAR ) + 1;
  1227. cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor);
  1228. cwsCurrentPath += "\\*";
  1229. }
  1230. /*
  1231. ** No files to be processed on this pass so go back and try to
  1232. ** find another or leave the loop as we've finished the task.
  1233. */
  1234. continue;
  1235. }
  1236. ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - FindNextFileW" );
  1237. cwsCurrentPath.GetBufferSetLength( iCurrentPathCursor );
  1238. cwsCurrentPath += FileFindData.cFileName;
  1239. }
  1240. if (FileFindData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  1241. {
  1242. SetFileAttributesW (cwsCurrentPath,
  1243. FileFindData.dwFileAttributes ^ (FILE_ATTRIBUTE_READONLY));
  1244. }
  1245. if ( !NameIsDotOrDotDot( FileFindData.cFileName ) )
  1246. {
  1247. if ( ( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) ||
  1248. !( FileFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
  1249. {
  1250. ft.Trace( VSSDBG_WRTCMN, L"DeleteFileW( %s, ... )", cwsCurrentPath.c_str() );
  1251. bSucceeded = DeleteFileW( cwsCurrentPath );
  1252. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1253. ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - DeleteFileW" );
  1254. }
  1255. else
  1256. {
  1257. ft.Trace( VSSDBG_WRTCMN, L"RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() );
  1258. bSucceeded = RemoveDirectoryW( cwsCurrentPath );
  1259. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1260. if ( HRESULT_FROM_WIN32( ERROR_DIR_NOT_EMPTY ) == ft.hr )
  1261. {
  1262. ft.Trace( VSSDBG_WRTCMN, L"Dir not empty after calling RemoveDirectoryW( %s, ... )", cwsCurrentPath.c_str() );
  1263. /*
  1264. ** The directory wasn't empty so move down one level,
  1265. ** close the old scan and start a new one.
  1266. */
  1267. FindClose (hFileScan);
  1268. hFileScan = INVALID_HANDLE_VALUE;
  1269. cwsCurrentPath += DIR_SEP_STRING L"*";
  1270. iCurrentPathCursor = cwsCurrentPath.GetLength() - 1;
  1271. dwSubDirectoriesEntered++;
  1272. }
  1273. else
  1274. {
  1275. ft.CheckForError( VSSDBG_WRTCMN, L"RemoveDirectoryTree - RemoveDirectoryW" );
  1276. }
  1277. }
  1278. }
  1279. }
  1280. }
  1281. VSS_STANDARD_CATCH(ft)
  1282. if (!HandleInvalid (hFileScan))
  1283. FindClose (hFileScan);
  1284. return( ft.hr );
  1285. } /* RemoveDirectoryTree () */
  1286. /*
  1287. **++
  1288. **
  1289. ** Routine Description:
  1290. **
  1291. ** Routines to construct and cleanup a security descriptor which
  1292. ** can be applied to limit access to an object to member of
  1293. ** either the Administrators or Backup Operators group.
  1294. **
  1295. **
  1296. ** Arguments:
  1297. **
  1298. ** psaSecurityAttributes Pointer to a SecurityAttributes
  1299. ** structure which has already been
  1300. ** setup to point to a blank
  1301. ** security descriptor
  1302. **
  1303. ** eSaType What we are building the SA for
  1304. **
  1305. ** bIncludeBackupOperator Whether or not to include an ACE to
  1306. ** grant BackupOperator access
  1307. **
  1308. **
  1309. ** Return Value:
  1310. **
  1311. ** Any HRESULT from
  1312. ** InitializeSecurityDescriptor()
  1313. ** AllocateAndInitializeSid()
  1314. ** SetEntriesInAcl()
  1315. ** SetSecurityDescriptorDacl()
  1316. **
  1317. **--
  1318. */
  1319. static HRESULT ConstructSecurityAttributes (
  1320. IN OUT PSECURITY_ATTRIBUTES psaSecurityAttributes,
  1321. IN BOOL bIncludeBackupOperator
  1322. )
  1323. {
  1324. DWORD dwStatus;
  1325. DWORD dwAccessMask = 0;
  1326. BOOL bSucceeded;
  1327. PSID psidBackupOperators = NULL;
  1328. PSID psidAdministrators = NULL;
  1329. PACL paclDiscretionaryAcl = NULL;
  1330. SID_IDENTIFIER_AUTHORITY sidNtAuthority = SECURITY_NT_AUTHORITY;
  1331. EXPLICIT_ACCESS eaExplicitAccess [2];
  1332. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"ConstructSecurityAttributes");
  1333. try
  1334. {
  1335. dwAccessMask = FILE_ALL_ACCESS;
  1336. /*
  1337. ** Initialise the security descriptor.
  1338. */
  1339. bSucceeded = ::InitializeSecurityDescriptor (
  1340. psaSecurityAttributes->lpSecurityDescriptor,
  1341. SECURITY_DESCRIPTOR_REVISION
  1342. );
  1343. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1344. ft.CheckForError( VSSDBG_WRTCMN, L"InitializeSecurityDescriptor" );
  1345. if ( bIncludeBackupOperator )
  1346. {
  1347. /*
  1348. ** Create a SID for the Backup Operators group.
  1349. */
  1350. bSucceeded = ::AllocateAndInitializeSid (
  1351. &sidNtAuthority,
  1352. 2,
  1353. SECURITY_BUILTIN_DOMAIN_RID,
  1354. DOMAIN_ALIAS_RID_BACKUP_OPS,
  1355. 0, 0, 0, 0, 0, 0,
  1356. &psidBackupOperators
  1357. );
  1358. ft.hr = GET_STATUS_FROM_BOOL ( bSucceeded );
  1359. ft.CheckForError( VSSDBG_WRTCMN, L"AllocateAndInitializeSid" );
  1360. }
  1361. /*
  1362. ** Create a SID for the Administrators group.
  1363. */
  1364. bSucceeded = ::AllocateAndInitializeSid (
  1365. &sidNtAuthority,
  1366. 2,
  1367. SECURITY_BUILTIN_DOMAIN_RID,
  1368. DOMAIN_ALIAS_RID_ADMINS,
  1369. 0, 0, 0, 0, 0, 0,
  1370. &psidAdministrators);
  1371. ft.hr = GET_STATUS_FROM_BOOL (bSucceeded);
  1372. ft.CheckForError( VSSDBG_WRTCMN, L"AllocateAndInitializeSid" );
  1373. /*
  1374. ** Initialize the array of EXPLICIT_ACCESS structures for an
  1375. ** ACEs we are setting.
  1376. **
  1377. ** The first ACE allows the Backup Operators group full access
  1378. ** and the second, allowa the Administrators group full
  1379. ** access.
  1380. */
  1381. eaExplicitAccess[0].grfAccessPermissions = dwAccessMask;
  1382. eaExplicitAccess[0].grfAccessMode = SET_ACCESS;
  1383. eaExplicitAccess[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  1384. eaExplicitAccess[0].Trustee.pMultipleTrustee = NULL;
  1385. eaExplicitAccess[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  1386. eaExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  1387. eaExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_ALIAS;
  1388. eaExplicitAccess[0].Trustee.ptstrName = (LPTSTR) psidAdministrators;
  1389. if ( bIncludeBackupOperator )
  1390. {
  1391. eaExplicitAccess[1].grfAccessPermissions = dwAccessMask;
  1392. eaExplicitAccess[1].grfAccessMode = SET_ACCESS;
  1393. eaExplicitAccess[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  1394. eaExplicitAccess[1].Trustee.pMultipleTrustee = NULL;
  1395. eaExplicitAccess[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
  1396. eaExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  1397. eaExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_ALIAS;
  1398. eaExplicitAccess[1].Trustee.ptstrName = (LPTSTR) psidBackupOperators;
  1399. }
  1400. /*
  1401. ** Create a new ACL that contains the new ACEs.
  1402. */
  1403. dwStatus = ::SetEntriesInAcl(
  1404. bIncludeBackupOperator ? 2 : 1,
  1405. eaExplicitAccess,
  1406. NULL,
  1407. &paclDiscretionaryAcl
  1408. );
  1409. ft.hr = HRESULT_FROM_WIN32 (dwStatus);
  1410. ft.CheckForError( VSSDBG_WRTCMN, L"SetEntriesInAcl" );
  1411. /*
  1412. ** Add the ACL to the security descriptor.
  1413. */
  1414. bSucceeded = ::SetSecurityDescriptorDacl (
  1415. psaSecurityAttributes->lpSecurityDescriptor,
  1416. TRUE,
  1417. paclDiscretionaryAcl,
  1418. FALSE
  1419. );
  1420. ft.hr = GET_STATUS_FROM_BOOL (bSucceeded);
  1421. ft.CheckForError( VSSDBG_WRTCMN, L"SetSecurityDescriptorDacl" );
  1422. paclDiscretionaryAcl = NULL;
  1423. bSucceeded = ::SetSecurityDescriptorControl (
  1424. psaSecurityAttributes->lpSecurityDescriptor,
  1425. SE_DACL_PROTECTED,
  1426. SE_DACL_PROTECTED);
  1427. ft.hr = GET_STATUS_FROM_BOOL (bSucceeded);
  1428. ft.CheckForError( VSSDBG_WRTCMN, L"SetSecurityDescriptorControl" );
  1429. }
  1430. VSS_STANDARD_CATCH( ft );
  1431. /*
  1432. ** Clean up any left over junk.
  1433. */
  1434. if ( NULL != psidAdministrators )
  1435. FreeSid ( psidAdministrators );
  1436. if ( NULL != psidBackupOperators )
  1437. FreeSid ( psidBackupOperators );
  1438. if ( NULL != paclDiscretionaryAcl )
  1439. LocalFree (paclDiscretionaryAcl );
  1440. return ( ft.hr );
  1441. } /* ConstructSecurityAttributes () */
  1442. static VOID CleanupSecurityAttributes(
  1443. IN PSECURITY_ATTRIBUTES psaSecurityAttributes
  1444. )
  1445. {
  1446. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CleanupSecurityAttributes");
  1447. BOOL bSucceeded;
  1448. BOOL bDaclPresent = FALSE;
  1449. BOOL bDaclDefaulted = TRUE;
  1450. PACL paclDiscretionaryAcl = NULL;
  1451. try
  1452. {
  1453. bSucceeded = ::GetSecurityDescriptorDacl(
  1454. psaSecurityAttributes->lpSecurityDescriptor,
  1455. &bDaclPresent,
  1456. &paclDiscretionaryAcl,
  1457. &bDaclDefaulted
  1458. );
  1459. if ( bSucceeded && bDaclPresent && !bDaclDefaulted && ( NULL != paclDiscretionaryAcl ) )
  1460. {
  1461. LocalFree( paclDiscretionaryAcl );
  1462. }
  1463. }
  1464. VSS_STANDARD_CATCH( ft );
  1465. } /* CleanupSecurityAttributes () */
  1466. /*
  1467. **++
  1468. **
  1469. ** Routine Description:
  1470. **
  1471. ** Creates a new target directory specified by the target path
  1472. ** member variable if not NULL. It will create any necessary
  1473. ** parent directories too.
  1474. **
  1475. ** NOTE: already exists type errors are ignored.
  1476. **
  1477. **
  1478. ** Arguments:
  1479. **
  1480. ** pwszTargetPath directory to create and apply security attributes
  1481. **
  1482. **
  1483. ** Return Value:
  1484. **
  1485. ** Any HRESULT resulting from memory allocation or directory creation attempts.
  1486. **--
  1487. */
  1488. HRESULT CreateTargetPath(
  1489. IN LPCWSTR pwszTargetPath
  1490. )
  1491. {
  1492. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CreateTargetPath");
  1493. ACL DiscretionaryAcl;
  1494. SECURITY_ATTRIBUTES saSecurityAttributes;
  1495. SECURITY_DESCRIPTOR sdSecurityDescriptor;
  1496. BOOL bSucceeded;
  1497. DWORD dwFileAttributes = 0;
  1498. const DWORD dwExtraAttributes = FILE_ATTRIBUTE_ARCHIVE |
  1499. FILE_ATTRIBUTE_HIDDEN |
  1500. FILE_ATTRIBUTE_SYSTEM |
  1501. FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
  1502. try
  1503. {
  1504. if ( NULL != pwszTargetPath )
  1505. {
  1506. /*
  1507. ** We really want a no access acl on this directory but
  1508. ** because of various problems with the EventLog and
  1509. ** ConfigDir writers we will settle for admin or backup
  1510. ** operator access only. The only possible accessor is
  1511. ** Backup which is supposed to have the SE_BACKUP_NAME
  1512. ** priv which will effectively bypass the ACL. No one else
  1513. ** needs to see this stuff.
  1514. */
  1515. saSecurityAttributes.nLength = sizeof (saSecurityAttributes);
  1516. saSecurityAttributes.lpSecurityDescriptor = &sdSecurityDescriptor;
  1517. saSecurityAttributes.bInheritHandle = FALSE;
  1518. ft.hr = ::ConstructSecurityAttributes( &saSecurityAttributes, FALSE );
  1519. ft.CheckForError( VSSDBG_WRTCMN, L"ConstructSecurityAttributes" );
  1520. CBsString expandedTarget;
  1521. ft.hr = GET_STATUS_FROM_BOOL(expandedTarget.ExpandEnvironmentStrings(pwszTargetPath));
  1522. ft.CheckForError(VSSDBG_WRTCMN, L"ExpandEnvironmentStrings");
  1523. bSucceeded = ::VsCreateDirectories (
  1524. expandedTarget,
  1525. &saSecurityAttributes,
  1526. dwExtraAttributes
  1527. );
  1528. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1529. if ( ft.hr == HRESULT_FROM_WIN32( ERROR_ALREADY_EXISTS ) )
  1530. {
  1531. ft.hr = S_OK;
  1532. }
  1533. ::CleanupSecurityAttributes( &saSecurityAttributes );
  1534. ft.CheckForError( VSSDBG_WRTCMN, L"VsCreateDirectories" );
  1535. }
  1536. }
  1537. VSS_STANDARD_CATCH( ft );
  1538. return( ft.hr );
  1539. } /* CreateTargetPath () */
  1540. /*
  1541. **++
  1542. **
  1543. ** Routine Description:
  1544. **
  1545. ** Deletes all the files present in the directory pointed at by the target
  1546. ** path member variable if not NULL. It will also remove the target directory
  1547. ** itself, eg for a target path of c:\dir1\dir2 all files under dir2 will be
  1548. ** removed and then dir2 itself will be deleted.
  1549. **
  1550. **
  1551. ** Arguments:
  1552. **
  1553. ** pwszTargetPath
  1554. **
  1555. **
  1556. ** Return Value:
  1557. **
  1558. ** Any HRESULT resulting from memory allocation or file and
  1559. ** directory deletion attempts.
  1560. **--
  1561. */
  1562. HRESULT CleanupTargetPath (LPCWSTR pwszTargetPath)
  1563. {
  1564. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"CleanupTargetPath");
  1565. HRESULT hrStatus = NOERROR;
  1566. DWORD dwFileAttributes = 0;
  1567. BOOL bSucceeded;
  1568. WCHAR wszTempBuffer [50];
  1569. UNICODE_STRING ucsTargetPath;
  1570. UNICODE_STRING ucsTargetPathAlternateName;
  1571. StringInitialise (&ucsTargetPath);
  1572. StringInitialise (&ucsTargetPathAlternateName);
  1573. if (NULL != pwszTargetPath)
  1574. {
  1575. hrStatus = StringCreateFromExpandedString (&ucsTargetPath,
  1576. pwszTargetPath,
  1577. MAX_PATH);
  1578. if (SUCCEEDED (hrStatus))
  1579. {
  1580. hrStatus = StringCreateFromString (&ucsTargetPathAlternateName,
  1581. &ucsTargetPath,
  1582. MAX_PATH);
  1583. }
  1584. if (SUCCEEDED (hrStatus))
  1585. {
  1586. dwFileAttributes = GetFileAttributesW (ucsTargetPath.Buffer);
  1587. hrStatus = GET_STATUS_FROM_BOOL ( -1 != dwFileAttributes);
  1588. if ((HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND) == hrStatus) ||
  1589. (HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND) == hrStatus))
  1590. {
  1591. hrStatus = NOERROR;
  1592. dwFileAttributes = 0;
  1593. }
  1594. else if (SUCCEEDED (hrStatus))
  1595. {
  1596. /*
  1597. ** If there is a file there then blow it away, or if it's
  1598. ** a directory, blow it and all it's contents away. This
  1599. ** is our directory and no one but us gets to play there.
  1600. */
  1601. hrStatus = RemoveDirectoryTree (CBsString(&ucsTargetPath));
  1602. if (FAILED (hrStatus))
  1603. {
  1604. srand ((unsigned) time (NULL));
  1605. _itow (rand (), wszTempBuffer, 16);
  1606. StringAppendString (&ucsTargetPathAlternateName, wszTempBuffer);
  1607. bSucceeded = MoveFileW (ucsTargetPath.Buffer,
  1608. ucsTargetPathAlternateName.Buffer);
  1609. if (bSucceeded)
  1610. {
  1611. BsDebugTraceAlways (0,
  1612. DEBUG_TRACE_VSSAPI,
  1613. (L"VSSAPI::CleanupTargetPath: "
  1614. L"FAILED to delete %s with status 0x%08X so renamed to %s",
  1615. ucsTargetPath.Buffer,
  1616. hrStatus,
  1617. ucsTargetPathAlternateName.Buffer));
  1618. }
  1619. else
  1620. {
  1621. BsDebugTraceAlways (0,
  1622. DEBUG_TRACE_VSSAPI,
  1623. (L"VSSAPI::CleanupTargetPath: "
  1624. L"FAILED to delete %s with status 0x%08X and "
  1625. L"FAILED to rename to %s with status 0x%08X",
  1626. ucsTargetPath.Buffer,
  1627. hrStatus,
  1628. ucsTargetPathAlternateName.Buffer,
  1629. GET_STATUS_FROM_BOOL (bSucceeded)));
  1630. }
  1631. }
  1632. }
  1633. }
  1634. }
  1635. StringFree (&ucsTargetPathAlternateName);
  1636. StringFree (&ucsTargetPath);
  1637. return (hrStatus);
  1638. } /* CleanupTargetPath () */
  1639. /*
  1640. **++
  1641. **
  1642. ** Routine Description:
  1643. **
  1644. ** Moves the contents of the source directory to the target directory.
  1645. **
  1646. ** Arguments:
  1647. **
  1648. ** pwszSourceDirectoryPath Source directory for the files to be moved
  1649. ** pwszTargetDirectoryPath Target directory for the files to be moved
  1650. **
  1651. **
  1652. ** Side Effects:
  1653. **
  1654. ** An intermediate error can leave directory in a partial moved
  1655. ** state where some of the files have been moved but not all.
  1656. **
  1657. **
  1658. ** Return Value:
  1659. **
  1660. ** Any HRESULT from FindFirstFile() etc or from MoveFileEx()
  1661. **
  1662. ** Remarks - copied from the wrtrshim\src\common.cpp file and
  1663. ** switched to using CBsStrings.
  1664. **--
  1665. */
  1666. HRESULT MoveFilesInDirectory (
  1667. IN CBsString cwsSourceDirectoryPath,
  1668. IN CBsString cwsTargetDirectoryPath
  1669. )
  1670. {
  1671. CVssFunctionTracer ft( VSSDBG_WRTCMN, L"MoveFilesInDirectory" );
  1672. HANDLE hFileScan = INVALID_HANDLE_VALUE;
  1673. try
  1674. {
  1675. WIN32_FIND_DATA sFileInformation;
  1676. if ( cwsSourceDirectoryPath.Tail() != DIR_SEP_CHAR )
  1677. cwsSourceDirectoryPath += DIR_SEP_CHAR;
  1678. if ( cwsTargetDirectoryPath.Tail() != DIR_SEP_CHAR )
  1679. cwsTargetDirectoryPath += DIR_SEP_CHAR;
  1680. hFileScan = ::FindFirstFileW (
  1681. cwsSourceDirectoryPath + L'*',
  1682. &sFileInformation
  1683. );
  1684. ft.hr = GET_STATUS_FROM_BOOL( INVALID_HANDLE_VALUE != hFileScan );
  1685. ft.CheckForError( VSSDBG_WRTCMN, L"FindFirstFileW" );
  1686. BOOL bMoreFiles;
  1687. do
  1688. {
  1689. if ( !NameIsDotOrDotDot( sFileInformation.cFileName ) )
  1690. {
  1691. BOOL bSucceeded;
  1692. CBsString cwsSourceFile = cwsSourceDirectoryPath + sFileInformation.cFileName;
  1693. CBsString cwsTargetFile = cwsTargetDirectoryPath + sFileInformation.cFileName;
  1694. ft.Trace( VSSDBG_WRTCMN, L"Moving '%s' to '%s'", cwsSourceFile.c_str(), cwsTargetFile.c_str() );
  1695. bSucceeded = ::MoveFileExW(
  1696. cwsSourceDirectoryPath + sFileInformation.cFileName,
  1697. cwsTargetDirectoryPath + sFileInformation.cFileName,
  1698. MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
  1699. );
  1700. ft.hr = GET_STATUS_FROM_BOOL( bSucceeded );
  1701. ft.CheckForError( VSSDBG_WRTCMN, L"MoveFileExW" );
  1702. }
  1703. bMoreFiles = ::FindNextFileW( hFileScan, &sFileInformation );
  1704. } while ( bMoreFiles );
  1705. /*
  1706. ** If the last move operation was successful determine the
  1707. ** reason for terminating the scan. No need to report an
  1708. ** error if all that happened was that we have finished
  1709. ** what we were asked to do.
  1710. */
  1711. ft.hr = GET_STATUS_FROM_FILESCAN( bMoreFiles );
  1712. ft.CheckForError( VSSDBG_WRTCMN, L"FindNextFileW" );
  1713. }
  1714. VSS_STANDARD_CATCH( ft );
  1715. if ( hFileScan != INVALID_HANDLE_VALUE )
  1716. ::FindClose( hFileScan );
  1717. return( ft.hr );
  1718. }
  1719. /*
  1720. **++
  1721. **
  1722. ** Routine Description:
  1723. **
  1724. ** Checks a path against an array of pointers to volume names to
  1725. ** see if path is affected by any of the volumes in the array
  1726. **
  1727. **
  1728. ** Arguments:
  1729. **
  1730. ** pwszPath Path to be checked
  1731. ** ulVolumeCount Number of volumes in volume array
  1732. ** ppwszVolumeNamesArray address of the array
  1733. ** pbReturnedFoundInVolumeArray pointer to a location to store the
  1734. ** result of the check
  1735. **
  1736. **
  1737. ** Side Effects:
  1738. **
  1739. ** None
  1740. **
  1741. **
  1742. ** Return Value:
  1743. **
  1744. ** Any HRESULT from:-
  1745. ** GetVolumePathNameW()
  1746. ** GetVolumeNameForVolumeMountPoint()
  1747. **
  1748. **--
  1749. */
  1750. HRESULT IsPathInVolumeArray (IN LPCWSTR pwszPath,
  1751. IN const ULONG ulVolumeCount,
  1752. IN LPCWSTR *ppwszVolumeNamesArray,
  1753. OUT PBOOL pbReturnedFoundInVolumeArray)
  1754. {
  1755. CVssFunctionTracer ft(VSSDBG_WRTCMN, L"IsPathInVolumeArray");
  1756. HRESULT hrStatus = NOERROR;
  1757. BOOL bFound = FALSE;
  1758. BOOL bContinue = TRUE;
  1759. ULONG ulIndex;
  1760. WCHAR wszVolumeName [MAX_VOLUMENAME_LENGTH];
  1761. UNICODE_STRING ucsVolumeMountPoint;
  1762. StringInitialise (&ucsVolumeMountPoint);
  1763. if ((0 == ulVolumeCount) || (NULL == pbReturnedFoundInVolumeArray))
  1764. {
  1765. BS_ASSERT (false);
  1766. bContinue = FALSE;
  1767. }
  1768. if (bContinue)
  1769. {
  1770. /*
  1771. ** We need a string that is at least as big as the supplied
  1772. ** path.
  1773. */
  1774. hrStatus = StringAllocate (&ucsVolumeMountPoint, wcslen (pwszPath) * sizeof (WCHAR));
  1775. bContinue = SUCCEEDED (hrStatus);
  1776. }
  1777. if (bContinue)
  1778. {
  1779. /*
  1780. ** Get the volume mount point
  1781. */
  1782. bContinue = GetVolumePathNameW (pwszPath,
  1783. ucsVolumeMountPoint.Buffer,
  1784. ucsVolumeMountPoint.MaximumLength / sizeof (WCHAR));
  1785. hrStatus = GET_STATUS_FROM_BOOL (bContinue);
  1786. }
  1787. if (bContinue)
  1788. {
  1789. /*
  1790. ** Get the volume name
  1791. */
  1792. bContinue = GetVolumeNameForVolumeMountPointW (ucsVolumeMountPoint.Buffer,
  1793. wszVolumeName,
  1794. SIZEOF_ARRAY (wszVolumeName));
  1795. hrStatus = GET_STATUS_FROM_BOOL (bContinue);
  1796. }
  1797. if (bContinue)
  1798. {
  1799. /*
  1800. ** Search to see if that volume is within snapshotted volumes
  1801. */
  1802. for (ulIndex = 0; !bFound && (ulIndex < ulVolumeCount); ulIndex++)
  1803. {
  1804. BS_ASSERT (NULL != ppwszVolumeNamesArray [ulIndex]);
  1805. if (0 == wcscmp (wszVolumeName, ppwszVolumeNamesArray [ulIndex]))
  1806. {
  1807. bFound = TRUE;
  1808. }
  1809. }
  1810. }
  1811. RETURN_VALUE_IF_REQUIRED (pbReturnedFoundInVolumeArray, bFound);
  1812. StringFree (&ucsVolumeMountPoint);
  1813. return (hrStatus);
  1814. } /* IsPathInVolumeArray () */
  1815. /*
  1816. **++
  1817. **
  1818. ** Routine Description:
  1819. **
  1820. ** Routine to classify the many assorted internal writer errors
  1821. ** into one of the narrow set of responses a writer is permitted
  1822. ** to send back to the requestor.
  1823. **
  1824. **
  1825. ** Arguments:
  1826. **
  1827. ** hrStatus HRESULT to be classified
  1828. **
  1829. **
  1830. ** Return Value:
  1831. **
  1832. ** One of the following list depending upon the supplied status.
  1833. **
  1834. ** VSS_E_WRITERERROR_OUTOFRESOURCES
  1835. ** VSS_E_WRITERERROR_RETRYABLE
  1836. ** VSS_E_WRITERERROR_NONRETRYABLE
  1837. ** VSS_E_WRITERERROR_TIMEOUT
  1838. ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  1839. **
  1840. **
  1841. **--
  1842. */
  1843. const HRESULT ClassifyWriterFailure (HRESULT hrWriterFailure)
  1844. {
  1845. BOOL bStatusUpdated;
  1846. return (ClassifyWriterFailure (hrWriterFailure, bStatusUpdated));
  1847. } /* ClassifyWriterFailure () */
  1848. /*
  1849. **++
  1850. **
  1851. ** Routine Description:
  1852. **
  1853. ** Routine to classify the many assorted internal writer errors
  1854. ** into one of the narrow set of responses a writer is permitted
  1855. ** to send back to the requestor.
  1856. **
  1857. **
  1858. ** Arguments:
  1859. **
  1860. ** hrStatus HRESULT to be classified
  1861. ** bStatusUpdated TRUE if the status is re-mapped
  1862. **
  1863. **
  1864. ** Return Value:
  1865. **
  1866. ** One of the following list depending upon the supplied status.
  1867. **
  1868. ** VSS_E_WRITERERROR_OUTOFRESOURCES
  1869. ** VSS_E_WRITERERROR_RETRYABLE
  1870. ** VSS_E_WRITERERROR_NONRETRYABLE
  1871. ** VSS_E_WRITERERROR_TIMEOUT
  1872. ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  1873. **
  1874. **
  1875. **--
  1876. */
  1877. const HRESULT ClassifyWriterFailure (HRESULT hrWriterFailure, BOOL &bStatusUpdated)
  1878. {
  1879. HRESULT hrStatus;
  1880. switch (hrWriterFailure)
  1881. {
  1882. case NOERROR:
  1883. case VSS_E_WRITERERROR_OUTOFRESOURCES:
  1884. case VSS_E_WRITERERROR_RETRYABLE:
  1885. case VSS_E_WRITERERROR_NONRETRYABLE:
  1886. case VSS_E_WRITERERROR_TIMEOUT:
  1887. case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT:
  1888. /*
  1889. ** These are ok as they are so no need to transmogrify them.
  1890. */
  1891. hrStatus = hrWriterFailure;
  1892. bStatusUpdated = FALSE;
  1893. break;
  1894. case E_OUTOFMEMORY:
  1895. case HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY):
  1896. case HRESULT_FROM_WIN32 (ERROR_NO_MORE_SEARCH_HANDLES):
  1897. case HRESULT_FROM_WIN32 (ERROR_NO_MORE_USER_HANDLES):
  1898. case HRESULT_FROM_WIN32 (ERROR_NO_LOG_SPACE):
  1899. case HRESULT_FROM_WIN32 (ERROR_DISK_FULL):
  1900. hrStatus = VSS_E_WRITERERROR_OUTOFRESOURCES;
  1901. bStatusUpdated = TRUE;
  1902. break;
  1903. case HRESULT_FROM_WIN32 (ERROR_NOT_READY):
  1904. hrStatus = VSS_E_WRITERERROR_RETRYABLE;
  1905. bStatusUpdated = TRUE;
  1906. break;
  1907. case HRESULT_FROM_WIN32 (ERROR_TIMEOUT):
  1908. hrStatus = VSS_E_WRITERERROR_TIMEOUT;
  1909. bStatusUpdated = TRUE;
  1910. break;
  1911. case E_UNEXPECTED:
  1912. case E_INVALIDARG: // equal to HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER)
  1913. case E_ACCESSDENIED:
  1914. case HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND):
  1915. case HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND):
  1916. case HRESULT_FROM_WIN32 (ERROR_PRIVILEGE_NOT_HELD):
  1917. case HRESULT_FROM_WIN32 (ERROR_NOT_LOCKED):
  1918. case HRESULT_FROM_WIN32 (ERROR_LOCKED):
  1919. default:
  1920. hrStatus = VSS_E_WRITERERROR_NONRETRYABLE;
  1921. bStatusUpdated = TRUE;
  1922. break;
  1923. }
  1924. return (hrStatus);
  1925. } /* ClassifyWriterFailure () */
  1926. /*
  1927. **++
  1928. **
  1929. ** Routine Description:
  1930. **
  1931. ** Routine to classify the many assorted internal shim errors
  1932. ** into one of the narrow set of responses a writer is permitted
  1933. ** to send back to the requestor.
  1934. **
  1935. **
  1936. ** Arguments:
  1937. **
  1938. ** hrStatus HRESULT to be classified
  1939. **
  1940. **
  1941. ** Return Value:
  1942. **
  1943. ** One of the following list depending upon the supplied status.
  1944. **
  1945. ** E_OUTOFMEMORY
  1946. ** E_ACCESSDENIED
  1947. ** E_INVALIDARG
  1948. ** E_UNEXPECTED
  1949. ** VSS_E_WRITERERROR_OUTOFRESOURCES
  1950. ** VSS_E_WRITERERROR_RETRYABLE
  1951. ** VSS_E_WRITERERROR_NONRETRYABLE
  1952. ** VSS_E_WRITERERROR_TIMEOUT
  1953. ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  1954. **--
  1955. */
  1956. const HRESULT ClassifyShimFailure (HRESULT hrWriterFailure)
  1957. {
  1958. BOOL bStatusUpdated;
  1959. return (ClassifyShimFailure (hrWriterFailure, bStatusUpdated));
  1960. } /* ClassifyShimFailure () */
  1961. /*
  1962. **++
  1963. **
  1964. ** Routine Description:
  1965. **
  1966. ** Routine to classify the many assorted internal shim errors
  1967. ** into one of the narrow set of responses a writer is permitted
  1968. ** to send back to the requestor.
  1969. **
  1970. **
  1971. ** Arguments:
  1972. **
  1973. ** hrStatus HRESULT to be classified
  1974. ** bStatusUpdated TRUE if the status is re-mapped
  1975. **
  1976. **
  1977. ** Return Value:
  1978. **
  1979. ** One of the following list depending upon the supplied status.
  1980. **
  1981. ** E_OUTOFMEMORY
  1982. ** E_ACCESSDENIED
  1983. ** E_INVALIDARG
  1984. ** E_UNEXPECTED
  1985. ** VSS_E_BAD_STATE
  1986. ** VSS_E_SNAPSHOT_SET_IN_PROGRESS
  1987. ** VSS_E_WRITERERROR_OUTOFRESOURCES
  1988. ** VSS_E_WRITERERROR_RETRYABLE
  1989. ** VSS_E_WRITERERROR_NONRETRYABLE
  1990. ** VSS_E_WRITERERROR_TIMEOUT
  1991. ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  1992. **--
  1993. */
  1994. const HRESULT ClassifyShimFailure (HRESULT hrWriterFailure, BOOL &bStatusUpdated)
  1995. {
  1996. HRESULT hrStatus;
  1997. switch (hrWriterFailure)
  1998. {
  1999. case NOERROR:
  2000. case E_OUTOFMEMORY:
  2001. case E_ACCESSDENIED:
  2002. case E_INVALIDARG: // equal to HRESULT_FROM_WIN32 (ERROR_INVALID_PARAMETER)
  2003. case E_UNEXPECTED:
  2004. case VSS_E_BAD_STATE:
  2005. case VSS_E_SNAPSHOT_SET_IN_PROGRESS:
  2006. case VSS_E_WRITERERROR_RETRYABLE:
  2007. case VSS_E_WRITERERROR_NONRETRYABLE:
  2008. case VSS_E_WRITERERROR_TIMEOUT:
  2009. case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT:
  2010. case VSS_E_WRITERERROR_OUTOFRESOURCES:
  2011. /*
  2012. ** These are ok as they are so no need to transmogrify them.
  2013. */
  2014. hrStatus = hrWriterFailure;
  2015. bStatusUpdated = FALSE;
  2016. break;
  2017. case HRESULT_FROM_WIN32 (ERROR_NOT_LOCKED):
  2018. hrStatus = VSS_E_BAD_STATE;
  2019. bStatusUpdated = TRUE;
  2020. break;
  2021. case HRESULT_FROM_WIN32 (ERROR_LOCKED):
  2022. hrStatus = VSS_E_SNAPSHOT_SET_IN_PROGRESS;
  2023. bStatusUpdated = TRUE;
  2024. break;
  2025. case HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY):
  2026. case HRESULT_FROM_WIN32 (ERROR_NO_MORE_SEARCH_HANDLES):
  2027. case HRESULT_FROM_WIN32 (ERROR_NO_MORE_USER_HANDLES):
  2028. case HRESULT_FROM_WIN32 (ERROR_NO_LOG_SPACE):
  2029. case HRESULT_FROM_WIN32 (ERROR_DISK_FULL):
  2030. hrStatus = E_OUTOFMEMORY;
  2031. bStatusUpdated = TRUE;
  2032. break;
  2033. case HRESULT_FROM_WIN32 (ERROR_PRIVILEGE_NOT_HELD):
  2034. hrStatus = E_ACCESSDENIED;
  2035. bStatusUpdated = TRUE;
  2036. break;
  2037. case HRESULT_FROM_WIN32 (ERROR_TIMEOUT):
  2038. case HRESULT_FROM_WIN32 (ERROR_PATH_NOT_FOUND):
  2039. case HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND):
  2040. case HRESULT_FROM_WIN32 (ERROR_NOT_READY):
  2041. default:
  2042. hrStatus = E_UNEXPECTED;
  2043. bStatusUpdated = TRUE;
  2044. break;
  2045. }
  2046. return (hrStatus);
  2047. } /* ClassifyShimFailure () */
  2048. /*
  2049. **++
  2050. **
  2051. ** Routine Description:
  2052. **
  2053. ** Routine to classify the many assorted internal shim or shim
  2054. ** writer errors into one of the narrow set of responses we are
  2055. ** permitted to send back to the requestor.
  2056. **
  2057. ** The determination is made to classify either as a shim error
  2058. ** or as a writer error based upon whether or not a writer name
  2059. ** is supplied. If it is supplied then the assumption is made
  2060. ** that this is a writer failure and so the error is classified
  2061. ** accordingly.
  2062. **
  2063. ** Note that this is a worker routine for the LogFailure() macro
  2064. ** and the two are intended to be used in concert.
  2065. **
  2066. **
  2067. ** Arguments:
  2068. **
  2069. ** pft Pointer to a Function trace class
  2070. ** pwszNameWriter The name of the applicable writer or NULL or L""
  2071. ** pwszNameCalledRoutine The name of the routine that returned the failure status
  2072. **
  2073. **
  2074. ** Side Effects:
  2075. **
  2076. ** hr field of *pft updated
  2077. **
  2078. **
  2079. ** Return Value:
  2080. **
  2081. ** One of the following list depending upon the supplied status.
  2082. **
  2083. ** E_OUTOFMEMORY
  2084. ** E_ACCESSDENIED
  2085. ** E_INVALIDARG
  2086. ** E_UNEXPECTED
  2087. ** VSS_E_BAD_STATE
  2088. ** VSS_E_SNAPSHOT_SET_IN_PROGRESS
  2089. ** VSS_E_WRITERERROR_OUTOFRESOURCES
  2090. ** VSS_E_WRITERERROR_RETRYABLE
  2091. ** VSS_E_WRITERERROR_NONRETRYABLE
  2092. ** VSS_E_WRITERERROR_TIMEOUT
  2093. ** VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT
  2094. **
  2095. **--
  2096. */
  2097. HRESULT LogFailureWorker (CVssFunctionTracer *pft,
  2098. LPCWSTR pwszNameWriter,
  2099. LPCWSTR pwszNameCalledRoutine)
  2100. {
  2101. if (pft->HrFailed ())
  2102. {
  2103. BOOL bStatusRemapped;
  2104. HRESULT hrStatusClassified = ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0]))
  2105. ? ClassifyShimFailure (pft->hr, bStatusRemapped)
  2106. : ClassifyWriterFailure (pft->hr, bStatusRemapped);
  2107. if (bStatusRemapped)
  2108. {
  2109. if (((NULL == pwszNameCalledRoutine) || (L'\0' == pwszNameCalledRoutine [0])) &&
  2110. ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0])))
  2111. {
  2112. pft->LogError (VSS_ERROR_SHIM_GENERAL_FAILURE,
  2113. VSSDBG_WRTCMN << pft->hr << hrStatusClassified);
  2114. pft->Trace (VSSDBG_WRTCMN,
  2115. L"FAILED with status 0x%08lX (converted to 0x%08lX)",
  2116. pft->hr,
  2117. hrStatusClassified);
  2118. }
  2119. else if ((NULL == pwszNameCalledRoutine) || (L'\0' == pwszNameCalledRoutine [0]))
  2120. {
  2121. pft->LogError (VSS_ERROR_SHIM_WRITER_GENERAL_FAILURE,
  2122. VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameWriter);
  2123. pft->Trace (VSSDBG_WRTCMN,
  2124. L"FAILED in writer %s with status 0x%08lX (converted to 0x%08lX)",
  2125. pwszNameWriter,
  2126. pft->hr,
  2127. hrStatusClassified);
  2128. }
  2129. else if ((NULL == pwszNameWriter) || (L'\0' == pwszNameWriter [0]))
  2130. {
  2131. pft->LogError (VSS_ERROR_SHIM_FAILED_SYSTEM_CALL,
  2132. VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameCalledRoutine);
  2133. pft->Trace (VSSDBG_WRTCMN,
  2134. L"FAILED calling routine %s with status 0x%08lX (converted to 0x%08lX)",
  2135. pwszNameCalledRoutine,
  2136. pft->hr,
  2137. hrStatusClassified);
  2138. }
  2139. else
  2140. {
  2141. pft->LogError (VSS_ERROR_SHIM_WRITER_FAILED_SYSTEM_CALL,
  2142. VSSDBG_WRTCMN << pft->hr << hrStatusClassified << pwszNameWriter << pwszNameCalledRoutine);
  2143. pft->Trace (VSSDBG_WRTCMN,
  2144. L"FAILED in writer %s calling routine %s with status 0x%08lX (converted to 0x%08lX)",
  2145. pwszNameWriter,
  2146. pwszNameCalledRoutine,
  2147. pft->hr,
  2148. hrStatusClassified);
  2149. }
  2150. pft->hr = hrStatusClassified;
  2151. }
  2152. }
  2153. return (pft->hr);
  2154. } /* LogFailureWorker () */