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.

497 lines
16 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. jbenton 03/11/2002 Modified for more general use
  14. --*/
  15. #ifndef __VSS_VOL_GEN_HXX__
  16. #define __VSS_VOL_GEN_HXX__
  17. #if _MSC_VER > 1000
  18. #pragma once
  19. #endif
  20. //#include <vss.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. // Inlines
  66. inline bool GetVolumeGuid( IN LPCWSTR pwszVolMgmtVolumeName, GUID& VolumeGuid )
  67. /*
  68. Routine description:
  69. Return the guid that corresponds to a mount manager volume name
  70. The volume is in the following format:
  71. \\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\
  72. */
  73. {
  74. BS_ASSERT(pwszVolMgmtVolumeName);
  75. // test the length
  76. if (::wcslen(pwszVolMgmtVolumeName) != x_nLengthOfVolMgmtVolumeName)
  77. return false;
  78. // test the definition
  79. if (::wcsncmp(pwszVolMgmtVolumeName,
  80. x_wszVolMgmtVolumeDefinition,
  81. STRING_LEN(x_wszVolMgmtVolumeDefinition) ) != 0)
  82. return false;
  83. pwszVolMgmtVolumeName += STRING_LEN(x_wszVolMgmtVolumeDefinition); // go to the GUID
  84. // test the end
  85. if(::wcsncmp(pwszVolMgmtVolumeName + STRING_LEN(x_wszGuidDefinition),
  86. x_wszVolMgmtEndOfVolumeName, ARRAY_LEN(x_wszVolMgmtEndOfVolumeName)) != 0)
  87. return false;
  88. // test the guid. If W2OLE fails, a SE exception is thrown.
  89. WCHAR wszGuid[ARRAY_LEN(x_wszGuidDefinition)];
  90. ::wcsncpy(wszGuid, pwszVolMgmtVolumeName, STRING_LEN(x_wszGuidDefinition));
  91. wszGuid[STRING_LEN(x_wszGuidDefinition)] = L'\0';
  92. BOOL bRes = ::CLSIDFromString(W2OLE(wszGuid), &VolumeGuid) == S_OK;
  93. return !!bRes;
  94. }
  95. inline bool GetVolumeGuid2( IN LPCWSTR pwszWinObjVolumeName, GUID& VolumeGuid )
  96. /*
  97. Routine description:
  98. Return the guid that corresponds to a mount manager volume name
  99. The format:
  100. \\??\\Volume{00000000-0000-0000-0000-000000000000}
  101. */
  102. {
  103. BS_ASSERT(pwszWinObjVolumeName);
  104. // test the length
  105. if (::wcslen(pwszWinObjVolumeName) != x_nLengthOfWinObjVolumeName)
  106. return false;
  107. // test the definition
  108. if (::wcsncmp(pwszWinObjVolumeName,
  109. x_wszWinObjVolumeDefinition,
  110. STRING_LEN(x_wszWinObjVolumeDefinition) ) != 0)
  111. return false;
  112. pwszWinObjVolumeName += STRING_LEN(x_wszWinObjVolumeDefinition); // go to the GUID
  113. // test the end
  114. if(::wcsncmp(pwszWinObjVolumeName + STRING_LEN(x_wszGuidDefinition),
  115. x_wszWinObjEndOfVolumeName, ARRAY_LEN(x_wszWinObjEndOfVolumeName)) != 0)
  116. return false;
  117. // test the guid. If W2OLE fails, a SE exception is thrown.
  118. WCHAR wszGuid[ARRAY_LEN(x_wszGuidDefinition)];
  119. ::wcsncpy(wszGuid, pwszWinObjVolumeName, STRING_LEN(x_wszGuidDefinition));
  120. wszGuid[STRING_LEN(x_wszGuidDefinition)] = L'\0';
  121. BOOL bRes = ::CLSIDFromString(W2OLE(wszGuid), &VolumeGuid) == S_OK;
  122. return !!bRes;
  123. }
  124. inline bool IsVolMgmtVolumeName( IN LPCWSTR pwszVolMgmtVolumeName )
  125. /*
  126. Routine description:
  127. Return true if the given name is a mount manager volume name, in format
  128. "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\"
  129. */
  130. {
  131. GUID VolumeGuid;
  132. BS_ASSERT(pwszVolMgmtVolumeName);
  133. return ::GetVolumeGuid(pwszVolMgmtVolumeName, VolumeGuid);
  134. }
  135. inline bool ConvertVolMgmtVolumeNameIntoKernelObject( IN LPWSTR pwszVolMgmtVolumeName )
  136. /*
  137. Routine description:
  138. Converts a mount manager volume name
  139. "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\"
  140. into the kernel mode format
  141. "\\??\\Volume{00000000-0000-0000-0000-000000000000}"
  142. */
  143. {
  144. // Check if the volume name is in teh proper format
  145. if (!IsVolMgmtVolumeName(pwszVolMgmtVolumeName))
  146. return false;
  147. // Eliminate the last backslash from the volume name.
  148. BS_ASSERT(pwszVolMgmtVolumeName[0] != L'\0');
  149. BS_ASSERT(::wcslen(pwszVolMgmtVolumeName) == x_nLengthOfVolMgmtVolumeName);
  150. BS_ASSERT(pwszVolMgmtVolumeName[x_nLengthOfVolMgmtVolumeName - 1] == L'\\');
  151. pwszVolMgmtVolumeName[x_nLengthOfVolMgmtVolumeName - 1] = L'\0';
  152. // Put the '?' sign
  153. BS_ASSERT(pwszVolMgmtVolumeName[1] == L'\\');
  154. pwszVolMgmtVolumeName[1] = L'?';
  155. return true;
  156. }
  157. #pragma warning(push)
  158. #pragma warning(disable:4296)
  159. inline void CopyVolumeName(
  160. PBYTE pDestination,
  161. LPWSTR pwszVolumeName
  162. ) throw(HRESULT)
  163. /*
  164. Routine description:
  165. Take the volume name as a Vol Mgmt volume name and copy it to the destination buffer as a
  166. WinObj volume name
  167. */
  168. {
  169. // Copy the volume definition part
  170. ::CopyMemory( pDestination, x_wszWinObjVolumeDefinition, STRING_SIZE(x_wszWinObjVolumeDefinition) );
  171. pDestination += STRING_SIZE(x_wszWinObjVolumeDefinition);
  172. BS_ASSERT(::wcslen(pwszVolumeName) == x_nSizeOfVolMgmtVolumeName/sizeof(WCHAR));
  173. BS_ASSERT(::wcsncmp(pwszVolumeName, x_wszVolMgmtVolumeDefinition,
  174. STRING_LEN(x_wszVolMgmtVolumeDefinition) ) == 0);
  175. pwszVolumeName += STRING_LEN(x_wszVolMgmtVolumeDefinition); // go directly to the GUID
  176. // Copy the guid
  177. ::CopyMemory( pDestination, pwszVolumeName, STRING_SIZE(x_wszGuidDefinition) );
  178. // Copy the end of volume name
  179. if (STRING_SIZE(x_wszWinObjEndOfVolumeName) > 0)
  180. {
  181. pDestination += STRING_SIZE(x_wszGuidDefinition);
  182. ::CopyMemory( pDestination, x_wszWinObjEndOfVolumeName, STRING_SIZE(x_wszWinObjEndOfVolumeName) );
  183. }
  184. }
  185. #pragma warning(pop)
  186. inline void VssGetVolumeDisplayName(
  187. IN WCHAR* wszVolumeName,
  188. OUT WCHAR* wszVolumeDisplayName, // Transfer ownership
  189. IN LONG lVolumeDisplayNameLen
  190. ) throw(HRESULT)
  191. {
  192. CVssFunctionTracer ft( VSSDBG_GEN, L"VssGetVolumeDisplayName" );
  193. HANDLE hSearch = INVALID_HANDLE_VALUE;
  194. WCHAR* wszVolumeMountPoints = NULL;
  195. try
  196. {
  197. BS_ASSERT(wszVolumeName && ::wcslen(wszVolumeName));
  198. BS_ASSERT(wszVolumeDisplayName);
  199. BS_ASSERT(lVolumeDisplayNameLen > 0);
  200. //
  201. // Get the list of all mount points of hte given volume.
  202. //
  203. // Get the length of the array
  204. DWORD dwVolumesBufferLen = 0;
  205. BOOL bResult = GetVolumePathNamesForVolumeName(wszVolumeName, NULL, 0, &dwVolumesBufferLen);
  206. if (!bResult && (GetLastError() != ERROR_MORE_DATA))
  207. ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  208. L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", wszVolumeName, &dwVolumesBufferLen);
  209. // Allocate the array
  210. wszVolumeMountPoints = ::VssAllocString(ft, dwVolumesBufferLen);
  211. // Get the mount points
  212. bResult = GetVolumePathNamesForVolumeName(wszVolumeName, wszVolumeMountPoints, dwVolumesBufferLen, NULL);
  213. if (!bResult)
  214. ft.TranslateGenericError(VSSDBG_GEN, HRESULT_FROM_WIN32(GetLastError()),
  215. L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", wszVolumeName, wszVolumeMountPoints, dwVolumesBufferLen);
  216. // If the volume has no mount points
  217. if ( !wszVolumeMountPoints[0] )
  218. {
  219. ::wcsncpy(wszVolumeDisplayName, wszVolumeName, lVolumeDisplayNameLen);
  220. }
  221. else
  222. {
  223. // else, if there is at least one mount point...
  224. // Get the smallest one
  225. WCHAR* pwszCurrentMountPoint = wszVolumeMountPoints;
  226. WCHAR* pwszSmallestMountPoint = wszVolumeMountPoints;
  227. LONG lSmallestMountPointLength = (LONG) ::wcslen(wszVolumeMountPoints);
  228. while(true)
  229. {
  230. // End of iteration?
  231. LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint);
  232. if (lCurrentMountPointLength == 0)
  233. break;
  234. // is this smaller?
  235. if (lCurrentMountPointLength < lSmallestMountPointLength)
  236. {
  237. pwszSmallestMountPoint = pwszCurrentMountPoint;
  238. lSmallestMountPointLength = lCurrentMountPointLength;
  239. }
  240. // Go to the next one. Skip the zero character.
  241. pwszCurrentMountPoint += lCurrentMountPointLength + 1;
  242. }
  243. ::wcsncpy(wszVolumeDisplayName, pwszSmallestMountPoint, lVolumeDisplayNameLen);
  244. }
  245. }
  246. VSS_STANDARD_CATCH(ft)
  247. ::VssFreeString(wszVolumeMountPoints);
  248. if (hSearch != INVALID_HANDLE_VALUE)
  249. FindVolumeMountPointClose(hSearch);
  250. if (ft.HrFailed())
  251. ft.Throw(VSSDBG_GEN, ft.hr, L"Rethrowing 0x%08lx", ft.hr);
  252. }
  253. void inline GetVolumeNameWithCheck(
  254. IN CVssDebugInfo dbgInfo, // Caller debugging info
  255. IN HRESULT hrToBeThrown, // The new HR to be thrown on error
  256. IN WCHAR* pwszVolumeMountPoint, // Mount point of hte volume terminated with '\\')
  257. OUT WCHAR* pwszVolumeName, // Returns the volume name
  258. IN DWORD dwVolumeNameBufferLength // length of pwszVolumeName including '\0'
  259. ) throw(HRESULT)
  260. /*++
  261. Description:
  262. Returns the volume name in "\\\\?\\Volume{00000000-0000-0000-0000-000000000000}\\" format
  263. --*/
  264. {
  265. CVssFunctionTracer ft(VSSDBG_GEN, L"GetVolumeNameWithCheck");
  266. BS_ASSERT(pwszVolumeMountPoint);
  267. BS_ASSERT(pwszVolumeName);
  268. BS_ASSERT(dwVolumeNameBufferLength >= x_nLengthOfVolMgmtVolumeName);
  269. pwszVolumeName[0] = L'\0';
  270. if (!::GetVolumeNameForVolumeMountPointW( pwszVolumeMountPoint,
  271. pwszVolumeName, dwVolumeNameBufferLength))
  272. ft.Throw( dbgInfo, hrToBeThrown,
  273. L"GetVolumeNameForVolumeMountPoint(%s,...) "
  274. L"failed with error code 0x%08lx", pwszVolumeMountPoint, GetLastError());
  275. BS_ASSERT(::wcslen(pwszVolumeName) != 0);
  276. BS_ASSERT(::IsVolMgmtVolumeName( pwszVolumeName ));
  277. }
  278. class CVssVolumeIterator
  279. {
  280. private:
  281. CVssVolumeIterator(const CVssVolumeIterator&);
  282. CVssVolumeIterator & operator=(const CVssVolumeIterator&);
  283. public:
  284. CVssVolumeIterator():
  285. m_SearchHandle(INVALID_HANDLE_VALUE),
  286. m_bFirstVolume(true)
  287. {};
  288. ~CVssVolumeIterator()
  289. {
  290. if (m_SearchHandle != INVALID_HANDLE_VALUE)
  291. ::FindVolumeClose(m_SearchHandle);
  292. m_SearchHandle = INVALID_HANDLE_VALUE;
  293. };
  294. bool SelectNewVolume(
  295. CVssFunctionTracer & ft,
  296. IN OUT LPWSTR wszNewVolumeInEnumeration,
  297. IN INT nMaxVolumeSize
  298. ) throw(HRESULT)
  299. {
  300. CVssFunctionTracer ft2( VSSDBG_GEN, L"CVssVolumeIterator::SelectNewVolume");
  301. while(true)
  302. {
  303. if (m_bFirstVolume)
  304. {
  305. m_bFirstVolume = false;
  306. BS_ASSERT(m_SearchHandle == INVALID_HANDLE_VALUE);
  307. // Note: We assume that there is at least one volume in the system. Otherwise we throw an error.
  308. m_SearchHandle = ::FindFirstVolumeW( wszNewVolumeInEnumeration, nMaxVolumeSize);
  309. if (m_SearchHandle == INVALID_HANDLE_VALUE)
  310. ft.Throw(VSSDBG_SWPRV,
  311. HRESULT_FROM_WIN32(GetLastError()),
  312. L"FindFirstVolumeW( [%s], MAX_PATH)", wszNewVolumeInEnumeration);
  313. // Check if the returned volume is valid
  314. if (!IsVolumeValid(wszNewVolumeInEnumeration))
  315. continue;
  316. return true;
  317. }
  318. else
  319. {
  320. BS_ASSERT(m_SearchHandle != INVALID_HANDLE_VALUE);
  321. if (!::FindNextVolumeW( m_SearchHandle, wszNewVolumeInEnumeration, nMaxVolumeSize) )
  322. {
  323. if (GetLastError() == ERROR_NO_MORE_FILES)
  324. return false; // End of iteration
  325. else
  326. ft.Throw( VSSDBG_SWPRV,
  327. HRESULT_FROM_WIN32(GetLastError()),
  328. L"FindNextVolumeW( %p, [%s], MAX_PATH)", m_SearchHandle, wszNewVolumeInEnumeration);
  329. }
  330. // Check if the returned volume is valid
  331. if (!IsVolumeValid(wszNewVolumeInEnumeration))
  332. continue;
  333. return true;
  334. }
  335. }
  336. }
  337. // Check if the volume is valid (a fixed, non-disconnected volume)
  338. bool IsVolumeValid(
  339. IN LPWSTR pwszVolumeName
  340. )
  341. {
  342. CVssFunctionTracer ft( VSSDBG_SWPRV, L"CVssVolumeIterator::IsVolumeValid");
  343. #ifdef FILTER_NON_FIXED_DRIVES
  344. // Check if the volume is fixed (i.e. no CD-ROM, no removable, etc.)
  345. UINT uDriveType = ::GetDriveTypeW(pwszVolumeName);
  346. if ( uDriveType != DRIVE_FIXED) {
  347. ft.Trace( VSSDBG_SWPRV, L"Warning: Ingnoring the non-fixed volume (%s) - %ud",
  348. pwszVolumeName, uDriveType);
  349. return false;
  350. }
  351. #endif
  352. #ifdef OPEN_VOLUME_CHECK
  353. // Try to open a handle to that volume
  354. CVssIOCTLChannel ichannel;
  355. ft.hr = ichannel.Open(ft, pwszVolumeName, true, false, VSS_ICHANNEL_LOG_NONE, 0);
  356. #endif
  357. // Log an error on invalid volumes.
  358. #ifdef LOG_ERROR
  359. if (ft.HrFailed())
  360. ft.LogError( VSS_WARNING_INVALID_VOLUME,
  361. VSSDBG_GEN << pwszVolumeName << ft.hr, EVENTLOG_WARNING_TYPE );
  362. #endif
  363. return (ft.HrSucceeded());
  364. }
  365. private:
  366. HANDLE m_SearchHandle;
  367. bool m_bFirstVolume;
  368. };
  369. #endif // __VSS_VOL_GEN_HXX__