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.

572 lines
18 KiB

  1. /*++
  2. Copyright (c) 1999-2001 Microsoft Corporation
  3. Module Name:
  4. vs_vol.hxx
  5. Abstract:
  6. Various defines for volume name management
  7. Author:
  8. Adi Oltean [aoltean] 10/10/1999
  9. Revision History:
  10. Name Date Comments
  11. aoltean 10/10/1999 Created
  12. aoltean 11/23/1999 Renamed to vs_vol.hxx from snap_vol.hxx
  13. --*/
  14. #ifndef __VSS_VOL_GEN_HXX__
  15. #define __VSS_VOL_GEN_HXX__
  16. #if _MSC_VER > 1000
  17. #pragma once
  18. #endif
  19. #include <vss.h>
  20. #include <vssmsg.h>
  21. ////////////////////////////////////////////////////////////////////////
  22. // Standard foo for file name aliasing. This code block must be after
  23. // all includes of VSS header files.
  24. //
  25. #ifdef VSS_FILE_ALIAS
  26. #undef VSS_FILE_ALIAS
  27. #endif
  28. #define VSS_FILE_ALIAS "INCVOLH"
  29. //
  30. ////////////////////////////////////////////////////////////////////////
  31. /////////////////////////////////////////////////////////////////////////////
  32. // Defines
  33. // Number of array elements
  34. #define ARRAY_LEN(A) (sizeof(A)/sizeof((A)[0]))
  35. // Size of strings, excepting the zero character
  36. #define STRING_SIZE(S) (sizeof(S) - sizeof((S)[0]))
  37. // String length (number of characters, excepting the zero character)
  38. #define STRING_LEN(S) (ARRAY_LEN(S) - 1)
  39. /////////////////////////////////////////////////////////////////////////////
  40. // Constants
  41. // guid def
  42. const WCHAR x_wszGuidDefinition[] = L"{00000000-0000-0000-0000-000000000000}";
  43. // Kernel volume name "\\??\\Volume{00000000-0000-0000-0000-000000000000}"
  44. const WCHAR x_wszWinObjVolumeDefinition[] = L"\\??\\Volume";
  45. const WCHAR x_wszWinObjEndOfVolumeName[] = L"";
  46. const WCHAR x_wszWinObjDosPathDefinition[] = L"\\??\\";
  47. // string size, without the L'\0' character
  48. const x_nSizeOfWinObjVolumeName = STRING_SIZE(x_wszWinObjVolumeDefinition) +
  49. STRING_SIZE(x_wszGuidDefinition) + STRING_SIZE(x_wszWinObjEndOfVolumeName);
  50. // string length, without the L'\0' character
  51. const x_nLengthOfWinObjVolumeName = STRING_LEN(x_wszWinObjVolumeDefinition) +
  52. STRING_LEN(x_wszGuidDefinition) + STRING_LEN(x_wszWinObjEndOfVolumeName);
  53. // Vol Mgmt volume name "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\"
  54. const WCHAR x_wszVolMgmtVolumeDefinition[] = L"\\\\?\\Volume";
  55. const WCHAR x_wszVolMgmtEndOfVolumeName[] = L"\\";
  56. // string size without the L'\0' character ( = 62 )
  57. const x_nSizeOfVolMgmtVolumeName = STRING_SIZE(x_wszVolMgmtVolumeDefinition) +
  58. STRING_SIZE(x_wszGuidDefinition) + STRING_SIZE(x_wszWinObjEndOfVolumeName);
  59. // string length, without the L'\0' character ( = 31)
  60. const x_nLengthOfVolMgmtVolumeName = STRING_LEN(x_wszVolMgmtVolumeDefinition) +
  61. STRING_LEN(x_wszGuidDefinition) + STRING_LEN(x_wszVolMgmtEndOfVolumeName);
  62. // Number of characters in an empty multisz string.
  63. const x_nEmptyVssMultiszLen = 2;
  64. //
  65. // Prefix constants for the global root.
  66. //
  67. const WCHAR x_wszGlobalRootPrefix[] = L"\\\\?\\GLOBALROOT";
  68. const size_t x_nGlobalRootPrefixLength = sizeof(x_wszGlobalRootPrefix)/sizeof(WCHAR) - 1;
  69. /////////////////////////////////////////////////////////////////////////////
  70. // Inlines
  71. inline bool GetVolumeGuid( IN LPCWSTR pwszVolMgmtVolumeName, GUID& VolumeGuid )
  72. /*
  73. Routine description:
  74. Return the guid that corresponds to a mount manager volume name
  75. The volume is in the following format:
  76. \\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\
  77. */
  78. {
  79. BS_ASSERT(pwszVolMgmtVolumeName);
  80. // test the length
  81. if (::wcslen(pwszVolMgmtVolumeName) != x_nLengthOfVolMgmtVolumeName)
  82. return false;
  83. // test the definition
  84. if (::wcsncmp(pwszVolMgmtVolumeName,
  85. x_wszVolMgmtVolumeDefinition,
  86. STRING_LEN(x_wszVolMgmtVolumeDefinition) ) != 0)
  87. return false;
  88. pwszVolMgmtVolumeName += STRING_LEN(x_wszVolMgmtVolumeDefinition); // go to the GUID
  89. // test the end
  90. if(::wcsncmp(pwszVolMgmtVolumeName + STRING_LEN(x_wszGuidDefinition),
  91. x_wszVolMgmtEndOfVolumeName, ARRAY_LEN(x_wszVolMgmtEndOfVolumeName)) != 0)
  92. return false;
  93. // test the guid. If W2OLE fails, a SE exception is thrown.
  94. WCHAR wszGuid[ARRAY_LEN(x_wszGuidDefinition)];
  95. ::wcsncpy(wszGuid, pwszVolMgmtVolumeName, STRING_LEN(x_wszGuidDefinition));
  96. wszGuid[STRING_LEN(x_wszGuidDefinition)] = L'\0';
  97. BOOL bRes = ::CLSIDFromString(W2OLE(wszGuid), &VolumeGuid) == S_OK;
  98. return !!bRes;
  99. }
  100. inline bool GetVolumeGuid2( IN LPCWSTR pwszWinObjVolumeName, GUID& VolumeGuid )
  101. /*
  102. Routine description:
  103. Return the guid that corresponds to a mount manager volume name
  104. The format:
  105. \\??\\Volume{00000000-0000-0000-0000-000000000000}
  106. */
  107. {
  108. BS_ASSERT(pwszWinObjVolumeName);
  109. // test the length
  110. if (::wcslen(pwszWinObjVolumeName) != x_nLengthOfWinObjVolumeName)
  111. return false;
  112. // test the definition
  113. if (::wcsncmp(pwszWinObjVolumeName,
  114. x_wszWinObjVolumeDefinition,
  115. STRING_LEN(x_wszWinObjVolumeDefinition) ) != 0)
  116. return false;
  117. pwszWinObjVolumeName += STRING_LEN(x_wszWinObjVolumeDefinition); // go to the GUID
  118. // test the end
  119. if(::wcsncmp(pwszWinObjVolumeName + STRING_LEN(x_wszGuidDefinition),
  120. x_wszWinObjEndOfVolumeName, ARRAY_LEN(x_wszWinObjEndOfVolumeName)) != 0)
  121. return false;
  122. // test the guid. If W2OLE fails, a SE exception is thrown.
  123. WCHAR wszGuid[ARRAY_LEN(x_wszGuidDefinition)];
  124. ::wcsncpy(wszGuid, pwszWinObjVolumeName, STRING_LEN(x_wszGuidDefinition));
  125. wszGuid[STRING_LEN(x_wszGuidDefinition)] = L'\0';
  126. BOOL bRes = ::CLSIDFromString(W2OLE(wszGuid), &VolumeGuid) == S_OK;
  127. return !!bRes;
  128. }
  129. inline bool IsVolMgmtVolumeName( IN LPCWSTR pwszVolMgmtVolumeName )
  130. /*
  131. Routine description:
  132. Return true if the given name is a mount manager volume name, in format
  133. "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\"
  134. */
  135. {
  136. GUID VolumeGuid;
  137. BS_ASSERT(pwszVolMgmtVolumeName);
  138. return ::GetVolumeGuid(pwszVolMgmtVolumeName, VolumeGuid);
  139. }
  140. inline bool ConvertVolMgmtVolumeNameIntoKernelObject( IN LPWSTR pwszVolMgmtVolumeName )
  141. /*
  142. Routine description:
  143. Converts a mount manager volume name
  144. "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\"
  145. into the kernel mode format
  146. "\\??\\Volume{00000000-0000-0000-0000-000000000000}"
  147. */
  148. {
  149. // Check if the volume name is in teh proper format
  150. if (!IsVolMgmtVolumeName(pwszVolMgmtVolumeName))
  151. return false;
  152. // Eliminate the last backslash from the volume name.
  153. BS_ASSERT(pwszVolMgmtVolumeName[0] != L'\0');
  154. BS_ASSERT(::wcslen(pwszVolMgmtVolumeName) == x_nLengthOfVolMgmtVolumeName);
  155. BS_ASSERT(pwszVolMgmtVolumeName[x_nLengthOfVolMgmtVolumeName - 1] == L'\\');
  156. pwszVolMgmtVolumeName[x_nLengthOfVolMgmtVolumeName - 1] = L'\0';
  157. // Put the '?' sign
  158. BS_ASSERT(pwszVolMgmtVolumeName[1] == L'\\');
  159. pwszVolMgmtVolumeName[1] = L'?';
  160. return true;
  161. }
  162. #pragma warning(push)
  163. #pragma warning(disable:4296)
  164. inline void CopyVolumeName(
  165. PBYTE pDestination,
  166. LPWSTR pwszVolumeName
  167. ) throw(HRESULT)
  168. /*
  169. Routine description:
  170. Take the volume name as a Vol Mgmt volume name and copy it to the destination buffer as a
  171. WinObj volume name
  172. */
  173. {
  174. // Copy the volume definition part
  175. ::CopyMemory( pDestination, x_wszWinObjVolumeDefinition, STRING_SIZE(x_wszWinObjVolumeDefinition) );
  176. pDestination += STRING_SIZE(x_wszWinObjVolumeDefinition);
  177. BS_ASSERT(::wcslen(pwszVolumeName) == x_nSizeOfVolMgmtVolumeName/sizeof(WCHAR));
  178. BS_ASSERT(::wcsncmp(pwszVolumeName, x_wszVolMgmtVolumeDefinition,
  179. STRING_LEN(x_wszVolMgmtVolumeDefinition) ) == 0);
  180. pwszVolumeName += STRING_LEN(x_wszVolMgmtVolumeDefinition); // go directly to the GUID
  181. // Copy the guid
  182. ::CopyMemory( pDestination, pwszVolumeName, STRING_SIZE(x_wszGuidDefinition) );
  183. // Copy the end of volume name
  184. if (STRING_SIZE(x_wszWinObjEndOfVolumeName) > 0)
  185. {
  186. pDestination += STRING_SIZE(x_wszGuidDefinition);
  187. ::CopyMemory( pDestination, x_wszWinObjEndOfVolumeName, STRING_SIZE(x_wszWinObjEndOfVolumeName) );
  188. }
  189. }
  190. #pragma warning(pop)
  191. inline void VerifyVolumeIsSupportedByVSS(
  192. IN LPWSTR pwszVolumeName
  193. ) throw( HRESULT )
  194. /*
  195. Routine description:
  196. Verify if a volume can be snapshotted.
  197. This is a check to be performed before invoking provider's IsVolumeSupported.
  198. Throws:
  199. VSS_E_VOLUME_NOT_SUPPORTED
  200. - if the volume is not supported by VSS.
  201. VSS_E_OBJECT_NOT_FOUND
  202. - The volume was not found
  203. */
  204. {
  205. CVssFunctionTracer ft( VSSDBG_GEN, L"VerifyVolumeIsSupportedByVSS" );
  206. // Check to make sure the volume is fixed (i.e. no CD-ROM, no removable)
  207. UINT uDriveType = ::GetDriveTypeW( pwszVolumeName );
  208. if ( uDriveType != DRIVE_FIXED) {
  209. ft.Throw( VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  210. L"Encountered a non-fixed volumes (%s) - %ud, not supported by VSS",
  211. pwszVolumeName, uDriveType );
  212. }
  213. DWORD dwFileSystemFlags = 0;
  214. if (!::GetVolumeInformationW(
  215. pwszVolumeName,
  216. NULL, // lpVolumeNameBuffer
  217. 0, // nVolumeNameSize
  218. NULL, // lpVolumeSerialNumber
  219. NULL, // lpMaximumComponentLength
  220. &dwFileSystemFlags,
  221. NULL,
  222. 0
  223. )) {
  224. // Check if the volume was found
  225. if ((GetLastError() == ERROR_NOT_READY) ||
  226. (GetLastError() == ERROR_DEVICE_NOT_CONNECTED))
  227. ft.Throw( VSSDBG_GEN, VSS_E_OBJECT_NOT_FOUND,
  228. L"Volume not found: error calling GetVolumeInformation on volume '%s' : %u",
  229. pwszVolumeName, GetLastError() );
  230. // RAW volumes are supported by VSS (bug 209059)
  231. if (GetLastError() != ERROR_UNRECOGNIZED_VOLUME)
  232. ft.Throw( VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  233. L"Error calling GetVolumeInformation on volume '%s' : %u",
  234. pwszVolumeName, GetLastError() );
  235. }
  236. // Read-only volumes are not supported
  237. if (dwFileSystemFlags & FILE_READ_ONLY_VOLUME) {
  238. ft.Throw( VSSDBG_GEN, VSS_E_VOLUME_NOT_SUPPORTED,
  239. L"Encountered a read-only volume (%s), not supported by VSS",
  240. pwszVolumeName);
  241. }
  242. }
  243. inline void VssGetVolumeDisplayName(
  244. IN VSS_PWSZ wszVolumeName,
  245. OUT VSS_PWSZ wszVolumeDisplayName, // Transfer ownership
  246. IN LONG lVolumeDisplayNameLen
  247. ) throw(HRESULT)
  248. {
  249. CVssFunctionTracer ft( VSSDBG_GEN, L"VssGetVolumeDisplayName" );
  250. VSS_PWSZ wszVolumeMountPoints = NULL;
  251. try
  252. {
  253. BS_ASSERT(wszVolumeName && ::wcslen(wszVolumeName));
  254. BS_ASSERT(wszVolumeDisplayName);
  255. BS_ASSERT(lVolumeDisplayNameLen > 0);
  256. //
  257. // Get the list of all mount points of hte given volume.
  258. //
  259. // Get the length of the array
  260. DWORD dwVolumesBufferLen = 0;
  261. BOOL bResult = GetVolumePathNamesForVolumeName(wszVolumeName, NULL, 0, &dwVolumesBufferLen);
  262. if (!bResult && (GetLastError() != ERROR_MORE_DATA))
  263. ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  264. L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", wszVolumeName, &dwVolumesBufferLen);
  265. // Allocate the array
  266. wszVolumeMountPoints = ::VssAllocString(ft, dwVolumesBufferLen);
  267. // Get the mount points
  268. bResult = GetVolumePathNamesForVolumeName(wszVolumeName, wszVolumeMountPoints, dwVolumesBufferLen, NULL);
  269. if (!bResult)
  270. ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  271. L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", wszVolumeName, wszVolumeMountPoints, dwVolumesBufferLen);
  272. // If the volume has no mount points
  273. if ( !wszVolumeMountPoints[0] )
  274. {
  275. ft.hr = ::StringCchCopyW(wszVolumeDisplayName, lVolumeDisplayNameLen, wszVolumeName);
  276. if (ft.HrFailed())
  277. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchCopyW");
  278. }
  279. else
  280. {
  281. // else, if there is at least one mount point...
  282. // Get the smallest one
  283. VSS_PWSZ pwszCurrentMountPoint = wszVolumeMountPoints;
  284. VSS_PWSZ pwszSmallestMountPoint = wszVolumeMountPoints;
  285. LONG lSmallestMountPointLength = (LONG) ::wcslen(wszVolumeMountPoints);
  286. while(true)
  287. {
  288. // End of iteration?
  289. LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint);
  290. if (lCurrentMountPointLength == 0)
  291. break;
  292. // is this smaller?
  293. if (lCurrentMountPointLength < lSmallestMountPointLength)
  294. {
  295. pwszSmallestMountPoint = pwszCurrentMountPoint;
  296. lSmallestMountPointLength = lCurrentMountPointLength;
  297. }
  298. // Go to the next one. Skip the zero character.
  299. pwszCurrentMountPoint += lCurrentMountPointLength + 1;
  300. }
  301. ft.hr = ::StringCchCopyW(wszVolumeDisplayName, lVolumeDisplayNameLen, pwszSmallestMountPoint);
  302. if (ft.HrFailed())
  303. ft.TranslateGenericError(VSSDBG_GEN, ft.hr, L"StringCchCopyW");
  304. }
  305. }
  306. VSS_STANDARD_CATCH(ft)
  307. ::VssFreeString(wszVolumeMountPoints);
  308. if (ft.HrFailed())
  309. ft.Throw(VSSDBG_GEN, ft.hr, L"Rethrowing 0x%08lx", ft.hr);
  310. }
  311. void inline GetVolumeNameWithCheck(
  312. IN CVssDebugInfo dbgInfo, // Caller debugging info
  313. IN HRESULT hrToBeThrown, // The new HR to be thrown on error
  314. IN WCHAR* pwszVolumeMountPoint, // Mount point of hte volume terminated with '\\')
  315. OUT WCHAR* pwszVolumeName, // Returns the volume name
  316. IN DWORD dwVolumeNameBufferLength // length of pwszVolumeName including '\0'
  317. ) throw(HRESULT)
  318. /*++
  319. Description:
  320. Returns the volume name in "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\" format
  321. --*/
  322. {
  323. CVssFunctionTracer ft(VSSDBG_GEN, L"GetVolumeNameWithCheck");
  324. BS_ASSERT(pwszVolumeMountPoint);
  325. BS_ASSERT(pwszVolumeName);
  326. BS_ASSERT(dwVolumeNameBufferLength >= x_nLengthOfVolMgmtVolumeName);
  327. pwszVolumeName[0] = L'\0';
  328. if (!::GetVolumeNameForVolumeMountPointW( pwszVolumeMountPoint,
  329. pwszVolumeName, dwVolumeNameBufferLength))
  330. ft.Throw( dbgInfo, hrToBeThrown,
  331. L"GetVolumeNameForVolumeMountPoint(%s,...) "
  332. L"failed with error code 0x%08lx", pwszVolumeMountPoint, GetLastError());
  333. BS_ASSERT(::wcslen(pwszVolumeName) != 0);
  334. BS_ASSERT(::IsVolMgmtVolumeName( pwszVolumeName ));
  335. }
  336. /////////////////////////////////////////////////////////////////////////////
  337. // CVssVolumeIterator - current version to be used only in Babbage
  338. // (since it throws VSS_E_PROVIDER_VETO on errors)
  339. class CVssVolumeIterator
  340. {
  341. private:
  342. CVssVolumeIterator(const CVssVolumeIterator&);
  343. CVssVolumeIterator & operator=(const CVssVolumeIterator&);
  344. public:
  345. CVssVolumeIterator():
  346. m_SearchHandle(INVALID_HANDLE_VALUE),
  347. m_bFirstVolume(true)
  348. {};
  349. ~CVssVolumeIterator()
  350. {
  351. if (m_SearchHandle != INVALID_HANDLE_VALUE)
  352. ::FindVolumeClose(m_SearchHandle);
  353. }
  354. bool SelectNewVolume(
  355. CVssFunctionTracer & ft,
  356. IN OUT LPWSTR wszNewVolumeInEnumeration,
  357. IN INT nMaxVolumeSize
  358. ) throw(HRESULT)
  359. {
  360. CVssFunctionTracer ft2( VSSDBG_GEN, L"CVssVolumeIterator::SelectNewVolume");
  361. // Only the software provider may call this iterator (for now)
  362. // In the future, if using theis class in other implementations please adapt the error handling.
  363. BS_ASSERT(ft.IsInSoftwareProvider());
  364. while(true)
  365. {
  366. if (m_bFirstVolume)
  367. {
  368. m_bFirstVolume = false;
  369. BS_ASSERT(m_SearchHandle == INVALID_HANDLE_VALUE);
  370. // Note: We assume that there is at least one volume in the system. Otherwise we throw an error.
  371. m_SearchHandle = ::FindFirstVolumeW( wszNewVolumeInEnumeration, nMaxVolumeSize);
  372. if (m_SearchHandle == INVALID_HANDLE_VALUE)
  373. ft.TranslateInternalProviderError( VSSDBG_SWPRV,
  374. HRESULT_FROM_WIN32(GetLastError()), VSS_E_PROVIDER_VETO,
  375. L"FindFirstVolumeW( [%s], MAX_PATH)", wszNewVolumeInEnumeration);
  376. // Check if the returned volume is valid
  377. if (!IsVolumeValid(wszNewVolumeInEnumeration))
  378. continue;
  379. return true;
  380. }
  381. else
  382. {
  383. BS_ASSERT(m_SearchHandle != INVALID_HANDLE_VALUE);
  384. if (!::FindNextVolumeW( m_SearchHandle, wszNewVolumeInEnumeration, nMaxVolumeSize) )
  385. {
  386. if (GetLastError() == ERROR_NO_MORE_FILES)
  387. return false; // End of iteration
  388. else
  389. ft.TranslateInternalProviderError( VSSDBG_SWPRV,
  390. HRESULT_FROM_WIN32(GetLastError()), VSS_E_PROVIDER_VETO,
  391. L"FindNextVolumeW( %p, [%s], MAX_PATH)", m_SearchHandle, wszNewVolumeInEnumeration);
  392. }
  393. // Check if the returned volume is valid
  394. if (!IsVolumeValid(wszNewVolumeInEnumeration))
  395. continue;
  396. return true;
  397. }
  398. }
  399. }
  400. // Check if the volume is valid (a fixed, non-disconnected volume)
  401. bool IsVolumeValid(
  402. IN LPWSTR pwszVolumeName
  403. )
  404. {
  405. CVssFunctionTracer ft( VSSDBG_SWPRV, L"CVssVolumeIterator::IsVolumeValid");
  406. // Check if the volume is fixed (i.e. no CD-ROM, no removable, etc.)
  407. UINT uDriveType = ::GetDriveTypeW(pwszVolumeName);
  408. if ( uDriveType != DRIVE_FIXED) {
  409. ft.Trace( VSSDBG_SWPRV, L"Warning: Ingnoring the non-fixed volume (%s) - %ud",
  410. pwszVolumeName, uDriveType);
  411. return false;
  412. }
  413. // Try to open a handle to that volume
  414. CVssIOCTLChannel ichannel;
  415. ft.hr = ichannel.Open(ft, pwszVolumeName, true, false, VSS_ICHANNEL_LOG_NONE, 0);
  416. // Log an error on invalid volumes.
  417. if (ft.HrFailed())
  418. ft.LogError( VSS_WARNING_INVALID_VOLUME,
  419. VSSDBG_GEN << pwszVolumeName << ft.hr, EVENTLOG_WARNING_TYPE );
  420. return (ft.HrSucceeded());
  421. }
  422. private:
  423. HANDLE m_SearchHandle;
  424. bool m_bFirstVolume;
  425. };
  426. #endif // __VSS_VOL_GEN_HXX__