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.

1112 lines
33 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2002-2004 Microsoft Corporation
  4. //
  5. // Module Name: volutil.cpp
  6. //
  7. // Description:
  8. // Utility functions for handling volumes
  9. //
  10. // Author: Jim Benton (jbenton) 30-Apr-2002
  11. //
  12. //////////////////////////////////////////////////////////////////////////////
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <map>
  17. #include "vs_assert.hxx"
  18. #include <atlbase.h>
  19. #include <winioctl.h>
  20. #include <ntddvol.h> // IOCTL_VOLUME_IS_OFFLINE
  21. #include <mountmgr.h> // MOUNTDEV_NAME
  22. #include <lm.h> // NetShareDel
  23. #include "vs_inc.hxx"
  24. #include <strsafe.h>
  25. #include "schema.h"
  26. #include "volutil.h"
  27. #define SYMBOLIC_LINK_LENGTH 28 // \DosDevices\X:
  28. #define GLOBALROOT_SIZE 14 // \\?\GLOBALROOT
  29. const WCHAR SETUP_KEY[] = L"SYSTEM\\Setup";
  30. const WCHAR SETUP_SYSTEMPARTITION[] = L"SystemPartition";
  31. BOOL GetVolumeDrive (
  32. IN WCHAR* pwszVolumePath,
  33. IN DWORD cchDriveName,
  34. OUT WCHAR* pwszDriveNameBuf
  35. )
  36. {
  37. CVssAutoPWSZ awszMountPoints;
  38. WCHAR* pwszCurrentMountPoint = NULL;
  39. BOOL fFound = FALSE;
  40. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeMountPointExists");
  41. // Get the length of the multi-string array
  42. DWORD cchVolumesBufferLen = 0;
  43. BOOL bResult = GetVolumePathNamesForVolumeName(
  44. pwszVolumePath,
  45. NULL,
  46. 0,
  47. &cchVolumesBufferLen);
  48. if (!bResult && (GetLastError() != ERROR_MORE_DATA))
  49. ft.TranslateGenericError(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()),
  50. L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolumePath, &cchVolumesBufferLen);
  51. // Allocate the array
  52. awszMountPoints.Allocate(cchVolumesBufferLen);
  53. // Get the mount points
  54. // Note: this API was introduced in WinXP so it will need to be replaced if backported
  55. bResult = GetVolumePathNamesForVolumeName(
  56. pwszVolumePath,
  57. awszMountPoints,
  58. cchVolumesBufferLen,
  59. NULL);
  60. if (!bResult)
  61. ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()),
  62. L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", pwszVolumePath, awszMountPoints, cchVolumesBufferLen);
  63. // If the volume has mount points
  64. pwszCurrentMountPoint = awszMountPoints;
  65. if ( pwszCurrentMountPoint[0] )
  66. {
  67. while(!fFound)
  68. {
  69. // End of iteration?
  70. LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint);
  71. if (lCurrentMountPointLength == 0)
  72. break;
  73. // Only a root directory should have a trailing backslash character
  74. if (lCurrentMountPointLength == 3 && pwszCurrentMountPoint[1] == L':' &&
  75. pwszCurrentMountPoint[2] == L'\\')
  76. {
  77. ft.hr = StringCchCopy(pwszDriveNameBuf, cchDriveName, pwszCurrentMountPoint);
  78. if (ft.HrFailed())
  79. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCChCopy failed %#x", ft.hr);
  80. fFound = TRUE;
  81. }
  82. // Go to the next one. Skip the zero character.
  83. pwszCurrentMountPoint += lCurrentMountPointLength + 1;
  84. }
  85. }
  86. return fFound;
  87. }
  88. BOOL
  89. VolumeSupportsQuotas(
  90. IN WCHAR* pwszVolume
  91. )
  92. {
  93. BOOL fSupportsQuotas = FALSE;
  94. DWORD dwDontCare = 0;
  95. DWORD dwFileSystemFlags = 0;
  96. _ASSERTE(pwszVolume != NULL);
  97. if (GetVolumeInformation(
  98. pwszVolume,
  99. NULL,
  100. 0,
  101. &dwDontCare,
  102. &dwDontCare,
  103. &dwFileSystemFlags,
  104. NULL,
  105. 0))
  106. {
  107. if (dwFileSystemFlags & FILE_VOLUME_QUOTAS)
  108. fSupportsQuotas = TRUE;
  109. }
  110. return fSupportsQuotas;
  111. }
  112. // Filter volumes where:
  113. // - the supporting device is disconnected
  114. // - the supporting device is a floppy
  115. // - the volume is not found
  116. // All other volumes are assumed valid
  117. BOOL
  118. VolumeIsValid(
  119. IN WCHAR* pwszVolume
  120. )
  121. {
  122. bool fValid = true;
  123. HANDLE hVol = INVALID_HANDLE_VALUE;
  124. DWORD cch = 0;
  125. DWORD dwRet = 0;
  126. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsValid");
  127. _ASSERTE(pwszVolume != NULL);
  128. cch = wcslen(pwszVolume);
  129. pwszVolume[cch - 1] = 0;
  130. hVol = CreateFile(pwszVolume, 0,
  131. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  132. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  133. pwszVolume[cch - 1] = '\\';
  134. dwRet = GetLastError();
  135. if (hVol == INVALID_HANDLE_VALUE && dwRet != ERROR_NOT_READY)
  136. {
  137. if (dwRet == ERROR_FILE_NOT_FOUND ||
  138. dwRet == ERROR_DEVICE_NOT_CONNECTED)
  139. {
  140. fValid = false;
  141. }
  142. else
  143. {
  144. ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, dwRet);
  145. }
  146. }
  147. if (hVol != INVALID_HANDLE_VALUE)
  148. CloseHandle(hVol);
  149. // Filter floppy drives
  150. if (fValid)
  151. {
  152. fValid = !VolumeIsFloppy(pwszVolume);
  153. }
  154. return fValid;
  155. }
  156. DWORD
  157. VolumeIsDirty(
  158. IN WCHAR* pwszVolume,
  159. OUT BOOL* pfDirty
  160. )
  161. {
  162. HANDLE hVol = INVALID_HANDLE_VALUE;
  163. DWORD dwRet = 0;
  164. DWORD cBytes = 0;
  165. DWORD dwResult = 0;
  166. WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE];
  167. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsDirty");
  168. _ASSERTE(pwszVolume != NULL);
  169. _ASSERTE(pfDirty != NULL);
  170. *pfDirty = FALSE;
  171. do
  172. {
  173. dwRet = GetDeviceName(pwszVolume, wszDeviceName);
  174. if (dwRet != ERROR_SUCCESS)
  175. {
  176. ft.hr = HRESULT_FROM_WIN32(dwRet);
  177. ft.Trace(VSSDBG_VSSADMIN, L"Unable to get volume device name %lS", pwszVolume);
  178. break;
  179. }
  180. hVol = CreateFile(wszDeviceName, GENERIC_READ,
  181. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  182. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  183. if (hVol != INVALID_HANDLE_VALUE)
  184. {
  185. if (DeviceIoControl(hVol, FSCTL_IS_VOLUME_DIRTY, NULL, 0, &dwResult, sizeof(dwResult), &cBytes, NULL))
  186. {
  187. *pfDirty = dwResult & VOLUME_IS_DIRTY;
  188. }
  189. else
  190. {
  191. dwRet = GetLastError();
  192. ft.Trace(VSSDBG_VSSADMIN, L"DeviceIoControl failed for device %lS, %#x", wszDeviceName, dwRet);
  193. break;
  194. }
  195. CloseHandle(hVol);
  196. }
  197. else
  198. {
  199. dwRet = GetLastError();
  200. ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, dwRet);
  201. break;
  202. }
  203. }
  204. while(false);
  205. return dwRet;
  206. }
  207. BOOL
  208. VolumeIsMountable(
  209. IN WCHAR* pwszVolume
  210. )
  211. {
  212. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsMountable");
  213. DWORD cch = 0;
  214. HANDLE hVol = INVALID_HANDLE_VALUE;
  215. BOOL bIsOffline = FALSE;
  216. DWORD bytes = 0;
  217. cch = wcslen(pwszVolume);
  218. pwszVolume[cch - 1] = 0;
  219. hVol = CreateFile(pwszVolume, 0,
  220. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  221. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  222. pwszVolume[cch - 1] = '\\';
  223. if (hVol != INVALID_HANDLE_VALUE)
  224. {
  225. bIsOffline = DeviceIoControl(hVol, IOCTL_VOLUME_IS_OFFLINE, NULL, 0, NULL, 0, &bytes,
  226. NULL);
  227. CloseHandle(hVol);
  228. }
  229. else
  230. {
  231. ft.Trace(VSSDBG_VSSADMIN, L"Unable to open volume %lS, %#x", pwszVolume, GetLastError());
  232. }
  233. return !bIsOffline;
  234. }
  235. BOOL
  236. VolumeHasMountPoints(
  237. IN WCHAR* pwszVolume
  238. )
  239. {
  240. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeHasMountPoints");
  241. DWORD dwVolumesBufferLen = 0;
  242. BOOL bResult = GetVolumePathNamesForVolumeName(
  243. pwszVolume,
  244. NULL,
  245. 0,
  246. &dwVolumesBufferLen);
  247. if (!bResult && (GetLastError() != ERROR_MORE_DATA))
  248. ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()),
  249. L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolume, &dwVolumesBufferLen);
  250. // More than three characters are needed to store just one mount point (multi-string buffer)
  251. // dwVolumesBufferLen == 1 typically for an un-mounted volume.
  252. return dwVolumesBufferLen > 3;
  253. }
  254. BOOL
  255. VolumeIsFloppy(
  256. WCHAR* pwszVolume
  257. )
  258. {
  259. HANDLE hDevice = INVALID_HANDLE_VALUE;
  260. WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE];
  261. NTSTATUS Status = ERROR_SUCCESS;
  262. IO_STATUS_BLOCK IoStatusBlock;
  263. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  264. BOOL fIsFloppy = FALSE;
  265. DWORD dwRet = GetDeviceName(pwszVolume, wszDeviceName);
  266. if (dwRet == ERROR_SUCCESS)
  267. {
  268. hDevice = CreateFile(wszDeviceName, FILE_READ_ATTRIBUTES,
  269. FILE_SHARE_READ |FILE_SHARE_WRITE, NULL,
  270. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  271. if (hDevice != INVALID_HANDLE_VALUE)
  272. {
  273. Status = NtQueryVolumeInformationFile(
  274. hDevice,
  275. &IoStatusBlock,
  276. &DeviceInfo,
  277. sizeof(DeviceInfo),
  278. FileFsDeviceInformation);
  279. if (NT_SUCCESS(Status))
  280. {
  281. if (DeviceInfo.DeviceType == FILE_DEVICE_DISK && DeviceInfo.Characteristics & FILE_FLOPPY_DISKETTE)
  282. {
  283. fIsFloppy = TRUE ;
  284. }
  285. }
  286. }
  287. }
  288. if (hDevice != INVALID_HANDLE_VALUE)
  289. CloseHandle(hDevice);
  290. return fIsFloppy;
  291. }
  292. BOOL
  293. VolumeIsReady(
  294. IN WCHAR* pwszVolume
  295. )
  296. {
  297. BOOL bIsReady = FALSE;
  298. DWORD dwDontCare;
  299. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsReady");
  300. if (GetVolumeInformation(
  301. pwszVolume,
  302. NULL,
  303. 0,
  304. &dwDontCare,
  305. &dwDontCare,
  306. &dwDontCare,
  307. NULL,
  308. 0))
  309. {
  310. bIsReady = TRUE;
  311. }
  312. else if (GetLastError() != ERROR_NOT_READY)
  313. ft.Trace(VSSDBG_VSSADMIN, L"GetVolumeInformation failed for volume %lS, %#x", pwszVolume, GetLastError());
  314. return bIsReady;
  315. }
  316. BOOL
  317. VolumeIsSystem(
  318. IN WCHAR* pwszVolume
  319. )
  320. {
  321. CRegKey cRegKeySetup;
  322. DWORD dwRet = 0;
  323. WCHAR wszRegDevice[MAX_PATH];
  324. WCHAR wszVolDevice[MAX_PATH+GLOBALROOT_SIZE];
  325. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeIsSystem");
  326. dwRet = cRegKeySetup.Open( HKEY_LOCAL_MACHINE, SETUP_KEY, KEY_READ);
  327. if (dwRet != ERROR_SUCCESS)
  328. {
  329. ft.hr = HRESULT_FROM_WIN32(dwRet);
  330. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Reg key open failed, %#x", dwRet);
  331. }
  332. DWORD dwLen = MAX_PATH;
  333. dwRet = cRegKeySetup.QueryValue(wszRegDevice, SETUP_SYSTEMPARTITION, &dwLen);
  334. if (dwRet != ERROR_SUCCESS)
  335. {
  336. ft.hr = HRESULT_FROM_WIN32(dwRet);
  337. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Reg key query failed, %#x", dwRet);
  338. }
  339. dwRet = GetDeviceName(pwszVolume, wszVolDevice);
  340. if (dwRet != ERROR_SUCCESS)
  341. {
  342. ft.hr = HRESULT_FROM_WIN32(dwRet);
  343. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unable to get volume device name, %lS, %#x", pwszVolume, dwRet);
  344. }
  345. if (_wcsicmp(wszVolDevice+GLOBALROOT_SIZE, wszRegDevice) == 0)
  346. return TRUE;
  347. return FALSE;
  348. }
  349. BOOL
  350. VolumeHoldsPagefile(
  351. IN WCHAR* pwszVolume
  352. )
  353. {
  354. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeHoldsPagefile");
  355. //
  356. // Retrieve page files.
  357. //
  358. BYTE* pbBuffer;
  359. DWORD dwBufferSize;
  360. PSYSTEM_PAGEFILE_INFORMATION pPageFileInfo = NULL;
  361. NTSTATUS status;
  362. BOOL fFound = FALSE;
  363. try
  364. {
  365. for (dwBufferSize=512; ; dwBufferSize += 512)
  366. {
  367. // Allocate buffer at 512 bytes increment. Previous allocation is
  368. // freed automatically.
  369. pbBuffer = (BYTE *) new BYTE[dwBufferSize];
  370. if ( pbBuffer==NULL )
  371. return E_OUTOFMEMORY;
  372. status = NtQuerySystemInformation(
  373. SystemPageFileInformation,
  374. pbBuffer,
  375. dwBufferSize,
  376. NULL
  377. );
  378. if ( status==STATUS_INFO_LENGTH_MISMATCH ) // buffer not big enough.
  379. {
  380. delete [] pbBuffer;
  381. continue;
  382. }
  383. pPageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION) pbBuffer;
  384. if (!NT_SUCCESS(status))
  385. {
  386. ft.hr = HRESULT_FROM_NT(status);
  387. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"NtQuerySystemInformation failed, %#x", status);
  388. }
  389. else
  390. break;
  391. }
  392. //
  393. // Walk through each of the page file volumes. Usually the return
  394. // looks like "\??\C:\pagefile.sys." If a pagefile is added\extended
  395. // \moved, the return will look like
  396. // "\Device\HarddiskVolume2\pagefile.sys."
  397. //
  398. WCHAR *p;
  399. WCHAR wszDrive[3] = L"?:";
  400. WCHAR buffer2[MAX_PATH], *pbuffer2 = buffer2;
  401. WCHAR wszVolDevice[MAX_PATH+GLOBALROOT_SIZE], *pwszVolDevice = wszVolDevice;
  402. DWORD dwRet;
  403. dwRet = GetDeviceName(pwszVolume, wszVolDevice);
  404. if (dwRet != ERROR_SUCCESS)
  405. {
  406. ft.hr = HRESULT_FROM_WIN32(dwRet);
  407. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unable to get volume device name, %lS, %#x", pwszVolume, dwRet);
  408. }
  409. for ( ; ; )
  410. {
  411. if ( pPageFileInfo==NULL ||
  412. pPageFileInfo->TotalSize==0 ) // We get 0 on WinPE.
  413. break;
  414. if ( pPageFileInfo->PageFileName.Length==0)
  415. break;
  416. p = wcschr(pPageFileInfo->PageFileName.Buffer, L':');
  417. if (p != NULL)
  418. {
  419. //
  420. // Convert drive letter to volume name.
  421. //
  422. _ASSERTE(p>pPageFileInfo->PageFileName.Buffer);
  423. _ASSERTE(towupper(*(p-1))>=L'A');
  424. _ASSERTE(towupper(*(p-1))<=L'Z');
  425. wszDrive[0] = towupper(*(p-1));
  426. dwRet = QueryDosDevice(wszDrive, buffer2, MAX_PATH);
  427. if (dwRet == 0)
  428. {
  429. ft.hr = HRESULT_FROM_NT(dwRet);
  430. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"QueryDosDevice failed, %#x", dwRet);
  431. }
  432. }
  433. else
  434. {
  435. _ASSERTE(_wcsnicmp(pPageFileInfo->PageFileName.Buffer,
  436. L"\\Device",
  437. 7)==0 );
  438. p = wcsstr(pPageFileInfo->PageFileName.Buffer,L"\\pagefile.sys");
  439. _ASSERTE( p!=NULL );
  440. if (p == NULL)
  441. {
  442. ft.hr = E_UNEXPECTED;
  443. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"Unexpected pagefile name format, %lS", pPageFileInfo->PageFileName.Buffer);
  444. }
  445. *p = L'\0';
  446. ft.hr = StringCchCopy(buffer2, MAX_PATH, pPageFileInfo->PageFileName.Buffer);
  447. if (ft.HrFailed())
  448. {
  449. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCchCopy failed, %#x", ft.hr);
  450. }
  451. }
  452. if (_wcsicmp(wszVolDevice+GLOBALROOT_SIZE, buffer2) == 0)
  453. {
  454. fFound = TRUE;
  455. break;
  456. }
  457. //
  458. // Next page file volume.
  459. //
  460. if (pPageFileInfo->NextEntryOffset == 0)
  461. break;
  462. pPageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)((PCHAR)pPageFileInfo
  463. + pPageFileInfo->NextEntryOffset);
  464. }
  465. }
  466. catch (...)
  467. {
  468. delete [] pbBuffer;
  469. throw;
  470. }
  471. delete [] pbBuffer;
  472. return fFound;
  473. }
  474. DWORD GetDeviceName(
  475. IN WCHAR* pwszVolume,
  476. OUT WCHAR wszDeviceName[MAX_PATH+GLOBALROOT_SIZE]
  477. )
  478. /*++
  479. Description:
  480. Get the device name of the given device.
  481. E.g. \Device\HarddiskVolume###
  482. Arguments:
  483. pwszVolume - volume GUID name.
  484. wszDeviceName - a buffer to receive device name. The buffer size must be
  485. MAX_PATH+GLOBALROOT_SIZE (including "\\?\GLOBALROOT").
  486. Return Value:
  487. Win32 errors
  488. --*/
  489. {
  490. DWORD dwRet;
  491. BOOL bRet;
  492. WCHAR wszMountDevName[MAX_PATH+sizeof(MOUNTDEV_NAME)];
  493. // Based on GetVolumeNameForRoot (in volmount.c), MAX_PATH seems to
  494. // be big enough as out buffer for IOCTL_MOUNTDEV_QUERY_DEVICE_NAME.
  495. // But we assume the size of device name could be as big as MAX_PATH-1,
  496. // so we allocate the buffer size to include MOUNTDEV_NAME size.
  497. PMOUNTDEV_NAME pMountDevName;
  498. DWORD dwBytesReturned;
  499. HANDLE hVol = INVALID_HANDLE_VALUE;
  500. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"GetDeviceName");
  501. DWORD cch = 0;
  502. _ASSERTE(pwszVolume != NULL);
  503. wszDeviceName[0] = L'\0';
  504. //
  505. // query the volume's device object name
  506. //
  507. cch = wcslen(pwszVolume);
  508. pwszVolume[cch - 1] = 0;
  509. hVol = CreateFile(pwszVolume, 0,
  510. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  511. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
  512. pwszVolume[cch - 1] = '\\';
  513. if (hVol != INVALID_HANDLE_VALUE)
  514. {
  515. bRet = DeviceIoControl(
  516. hVol, // handle to device
  517. IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
  518. NULL, // input data buffer
  519. 0, // size of input data buffer
  520. wszMountDevName, // output data buffer
  521. sizeof(wszMountDevName), // size of output data buffer
  522. &dwBytesReturned,
  523. NULL // overlapped information
  524. );
  525. dwRet = GetLastError();
  526. CloseHandle(hVol);
  527. if ( bRet==FALSE )
  528. {
  529. ft.Trace(VSSDBG_VSSADMIN, L"GetDeviceName: DeviceIoControl() failed: %X", dwRet);
  530. return dwRet;
  531. }
  532. pMountDevName = (PMOUNTDEV_NAME) wszMountDevName;
  533. if (pMountDevName->NameLength == 0)
  534. {
  535. // TODO: Is this possible? UNKNOWN?
  536. _ASSERTE( 0 );
  537. }
  538. else
  539. {
  540. //
  541. // copy name
  542. //
  543. ft.hr = StringCchPrintf(wszDeviceName, MAX_PATH+GLOBALROOT_SIZE, L"\\\\?\\GLOBALROOT" );
  544. if (ft.HrFailed())
  545. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"StringCchPrintf failed: %#x", ft.hr);
  546. CopyMemory(wszDeviceName+GLOBALROOT_SIZE,
  547. pMountDevName->Name,
  548. pMountDevName->NameLength
  549. );
  550. // appending terminating NULL
  551. wszDeviceName[pMountDevName->NameLength/2 + GLOBALROOT_SIZE] = L'\0';
  552. }
  553. }
  554. return ERROR_SUCCESS;
  555. }
  556. // VolumeMountPointExists is currently written specifically to verify the existence of a mountpoint
  557. // as defined by the WMI Win32_MountPoint Directory reference string. This string names the directory
  558. // such that the trailing backslash appears only on root directories. This is a basic assumption
  559. // made by this function. It is not general purpose. The calling code should be changed to append
  560. // the trailing backslash so that this function can be generalized. Mount points are enumerated
  561. // by GetVolumePathNamesForVolumeName API with trailing backslashes.
  562. BOOL
  563. VolumeMountPointExists(
  564. IN WCHAR* pwszVolume,
  565. IN WCHAR* pwszDirectory
  566. )
  567. {
  568. CVssAutoPWSZ awszMountPoints;
  569. WCHAR* pwszCurrentMountPoint = NULL;
  570. BOOL fFound = FALSE;
  571. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"VolumeMountPointExists");
  572. // Get the length of the multi-string array
  573. DWORD cchVolumesBufferLen = 0;
  574. BOOL bResult = GetVolumePathNamesForVolumeName(
  575. pwszVolume,
  576. NULL,
  577. 0,
  578. &cchVolumesBufferLen);
  579. if (!bResult && (GetLastError() != ERROR_MORE_DATA))
  580. ft.TranslateGenericError(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()),
  581. L"GetVolumePathNamesForVolumeName(%s, 0, 0, %p)", pwszVolume, &cchVolumesBufferLen);
  582. // Allocate the array
  583. awszMountPoints.Allocate(cchVolumesBufferLen);
  584. // Get the mount points
  585. // Note: this API was introduced in WinXP so it will need to be replaced if backported
  586. bResult = GetVolumePathNamesForVolumeName(
  587. pwszVolume,
  588. awszMountPoints,
  589. cchVolumesBufferLen,
  590. NULL);
  591. if (!bResult)
  592. ft.Throw(VSSDBG_VSSADMIN, HRESULT_FROM_WIN32(GetLastError()),
  593. L"GetVolumePathNamesForVolumeName(%s, %p, %lu, 0)", pwszVolume, awszMountPoints, cchVolumesBufferLen);
  594. // If the volume has mount points
  595. pwszCurrentMountPoint = awszMountPoints;
  596. if ( pwszCurrentMountPoint[0] )
  597. {
  598. while(true)
  599. {
  600. // End of iteration?
  601. LONG lCurrentMountPointLength = (LONG) ::wcslen(pwszCurrentMountPoint);
  602. if (lCurrentMountPointLength == 0)
  603. break;
  604. // Only a root directory should have a trailing backslash character
  605. if (lCurrentMountPointLength > 2 &&
  606. pwszCurrentMountPoint[lCurrentMountPointLength-1] == L'\\' &&
  607. pwszCurrentMountPoint[lCurrentMountPointLength-2] != L':')
  608. {
  609. pwszCurrentMountPoint[lCurrentMountPointLength-1] = L'\0';
  610. }
  611. if (_wcsicmp(pwszDirectory, pwszCurrentMountPoint) == 0)
  612. {
  613. fFound = TRUE;
  614. break;
  615. }
  616. // Go to the next one. Skip the zero character.
  617. pwszCurrentMountPoint += lCurrentMountPointLength + 1;
  618. }
  619. }
  620. return fFound;
  621. }
  622. void
  623. DeleteVolumeDriveLetter(
  624. IN WCHAR* pwszVolume,
  625. IN WCHAR* pwszDrivePath
  626. )
  627. {
  628. BOOL fVolumeLocked = FALSE;
  629. HANDLE hVolume = INVALID_HANDLE_VALUE;
  630. DWORD dwRet = 0;
  631. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"DeleteVolumeDriveLetter");
  632. _ASSERTE(pwszVolume != NULL);
  633. _ASSERTE(pwszDrivePath != NULL);
  634. // Try to lock the volume
  635. DWORD cch = wcslen(pwszVolume);
  636. pwszVolume[cch - 1] = 0;
  637. hVolume = CreateFile(
  638. pwszVolume,
  639. GENERIC_READ | GENERIC_WRITE,
  640. FILE_SHARE_READ | FILE_SHARE_WRITE,
  641. NULL,
  642. OPEN_EXISTING,
  643. NULL,
  644. NULL
  645. );
  646. pwszVolume[cch - 1] = '\\';
  647. if (hVolume == INVALID_HANDLE_VALUE)
  648. {
  649. dwRet = GetLastError();
  650. ft.hr = HRESULT_FROM_WIN32(dwRet);
  651. ft.Throw(VSSDBG_VSSADMIN, ft.hr,
  652. L"CreateFile(OPEN_EXISTING) failed for %lS, %#x", pwszVolume, dwRet);
  653. }
  654. dwRet = LockVolume(hVolume);
  655. if (dwRet == ERROR_SUCCESS)
  656. {
  657. fVolumeLocked = TRUE;
  658. ft.Trace(VSSDBG_VSSADMIN,
  659. L"volume %lS locked", pwszVolume);
  660. }
  661. else
  662. {
  663. ft.Trace(VSSDBG_VSSADMIN,
  664. L"Unable to lock volume %lS, %#x", pwszVolume, dwRet);
  665. }
  666. try
  667. {
  668. if (fVolumeLocked)
  669. {
  670. // If volume is locked delete the mount point
  671. if (!DeleteVolumeMountPoint(pwszDrivePath))
  672. {
  673. dwRet = GetLastError();
  674. ft.hr = HRESULT_FROM_WIN32(dwRet);
  675. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"DeleteVolumeMountPoint failed %#x, drivepath<%lS>", dwRet, pwszDrivePath);
  676. }
  677. }
  678. else
  679. {
  680. // Otherwise, remove the entry from the volume mgr database only
  681. // The volume will still be accessible through the drive letter until reboot
  682. ft.hr = DeleteDriveLetterFromDB(pwszDrivePath);
  683. if (ft.HrFailed())
  684. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"DeleteDriveLetterFromDB failed %#x, drivepath<%lS>", ft.hr, pwszDrivePath);
  685. }
  686. }
  687. catch (...)
  688. {
  689. CloseHandle(hVolume);
  690. throw;
  691. }
  692. CloseHandle(hVolume);
  693. }
  694. HRESULT
  695. DeleteDriveLetterFromDB(
  696. IN WCHAR* pwszDriveLetter)
  697. {
  698. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB");
  699. MOUNTMGR_MOUNT_POINT *InputMountPoint=NULL;
  700. MOUNTMGR_MOUNT_POINTS *OutputMountPoints=NULL;
  701. ULONG ulInputSize = 0;
  702. HANDLE hMountMgr = INVALID_HANDLE_VALUE;
  703. DWORD dwBytesReturned = 0;
  704. DWORD dwRet = 0;
  705. BOOL bRet = FALSE;
  706. _ASSERTE(pwszDriveLetter != NULL);
  707. //
  708. // Prepare IOCTL_MOUNTMGR_QUERY_POINTS input
  709. //
  710. ulInputSize = sizeof(MOUNTMGR_MOUNT_POINT) + SYMBOLIC_LINK_LENGTH;
  711. InputMountPoint = (MOUNTMGR_MOUNT_POINT *) new BYTE[ulInputSize];
  712. if ( InputMountPoint==NULL )
  713. {
  714. ft.hr = E_OUTOFMEMORY;
  715. return ft.hr;
  716. }
  717. ZeroMemory(InputMountPoint, ulInputSize);
  718. InputMountPoint->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
  719. InputMountPoint->SymbolicLinkNameLength = SYMBOLIC_LINK_LENGTH;
  720. InputMountPoint->UniqueIdOffset = 0;
  721. InputMountPoint->UniqueIdLength = 0;
  722. InputMountPoint->DeviceNameOffset = 0;
  723. InputMountPoint->DeviceNameLength = 0;
  724. //
  725. // Fill device name.
  726. //
  727. WCHAR wszBuffer[SYMBOLIC_LINK_LENGTH/2+1];
  728. LPWSTR pwszBuffer;
  729. pwszBuffer = (LPWSTR)((PCHAR)InputMountPoint +
  730. InputMountPoint->SymbolicLinkNameOffset);
  731. pwszDriveLetter[0] = towupper(pwszDriveLetter[0]);
  732. ft.hr = StringCchPrintf(wszBuffer, SYMBOLIC_LINK_LENGTH/2+1, L"\\DosDevices\\%c:", pwszDriveLetter[0] );
  733. if (ft.HrFailed())
  734. {
  735. ft.Trace(VSSDBG_VSSADMIN, L"StringCchPrintf failed %#x", ft.hr);
  736. goto _bailout;
  737. }
  738. memcpy(pwszBuffer, wszBuffer, SYMBOLIC_LINK_LENGTH);
  739. //
  740. // Allocate space for output
  741. //
  742. OutputMountPoints = (MOUNTMGR_MOUNT_POINTS *) new WCHAR[4096];
  743. if ( OutputMountPoints==NULL )
  744. {
  745. ft.hr = E_OUTOFMEMORY;
  746. goto _bailout;
  747. }
  748. //
  749. // Open mount manager
  750. //
  751. hMountMgr = CreateFile( MOUNTMGR_DOS_DEVICE_NAME,
  752. GENERIC_READ | GENERIC_WRITE,
  753. FILE_SHARE_READ | FILE_SHARE_WRITE,
  754. NULL,
  755. OPEN_EXISTING,
  756. FILE_ATTRIBUTE_NORMAL,
  757. NULL
  758. );
  759. if ( hMountMgr==INVALID_HANDLE_VALUE )
  760. {
  761. dwRet = GetLastError();
  762. ft.hr = HRESULT_FROM_WIN32(dwRet);
  763. ft.Trace(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB CreateFile failed %#x", dwRet);
  764. goto _bailout;
  765. }
  766. //
  767. // Issue IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY.
  768. //
  769. bRet = DeviceIoControl( hMountMgr,
  770. IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY,
  771. InputMountPoint,
  772. ulInputSize,
  773. OutputMountPoints,
  774. 4096 * sizeof(WCHAR),
  775. &dwBytesReturned,
  776. NULL
  777. );
  778. dwRet = GetLastError(); // Save error code.
  779. CloseHandle(hMountMgr);
  780. hMountMgr = NULL;
  781. if ( bRet==FALSE )
  782. {
  783. ft.hr = HRESULT_FROM_WIN32(dwRet);
  784. ft.Trace(VSSDBG_VSSADMIN, L"DeleteDriveLetterFromDB DeviceIoControl failed %#x", dwRet);
  785. goto _bailout;
  786. }
  787. delete [] InputMountPoint;
  788. delete [] OutputMountPoints;
  789. return S_OK;
  790. _bailout:
  791. delete [] InputMountPoint;
  792. delete [] OutputMountPoints;
  793. return ft.hr;
  794. }
  795. DWORD
  796. LockVolume(
  797. IN HANDLE hVolume
  798. )
  799. {
  800. DWORD dwBytes = 0;
  801. BOOL fRet = FALSE;
  802. int nCount = 0;
  803. while ( fRet==FALSE )
  804. {
  805. fRet = DeviceIoControl(
  806. hVolume,
  807. FSCTL_LOCK_VOLUME,
  808. NULL,
  809. 0,
  810. NULL,
  811. 0,
  812. &dwBytes,
  813. NULL
  814. );
  815. if (fRet == FALSE)
  816. {
  817. DWORD dwRet = GetLastError();
  818. if (dwRet != ERROR_ACCESS_DENIED || nCount>=60)
  819. {
  820. return dwRet;
  821. }
  822. nCount++;
  823. Sleep( 500 ); // Wait for half second up to 60 times (30s).
  824. }
  825. }
  826. return ERROR_SUCCESS;
  827. }
  828. // Returns TRUE if the drive letter is available
  829. BOOL IsDriveLetterAvailable (
  830. IN WCHAR* pwszDriveLetter
  831. )
  832. {
  833. int iLen = 0;
  834. BOOL fFound = FALSE;
  835. DWORD cchBufLen = 0;
  836. CVssAutoPWSZ awszDriveStrings;
  837. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsDriveLetterAvailable");
  838. _ASSERTE(pwszDriveLetter != NULL);
  839. // How much space needed for drive strings?
  840. cchBufLen = GetLogicalDriveStrings(0, NULL);
  841. if (cchBufLen == 0)
  842. {
  843. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  844. ft.Trace(VSSDBG_VSSADMIN, L"GetLogicalDriveStrings failed %#x", GetLastError());
  845. }
  846. else
  847. {
  848. // Allocate space for the drive strings
  849. awszDriveStrings.Allocate(cchBufLen);
  850. // Get the drive strings
  851. if (GetLogicalDriveStrings(cchBufLen, awszDriveStrings) == 0)
  852. {
  853. ft.hr = HRESULT_FROM_WIN32(GetLastError());
  854. ft.Trace(VSSDBG_VSSADMIN, L"GetLogicalDriveStrings failed %#x", GetLastError());
  855. }
  856. else
  857. {
  858. WCHAR* pwcTempDriveString = awszDriveStrings;
  859. WCHAR wcDriveLetter = towupper(pwszDriveLetter[0]);
  860. // Look for the drive letter in the list of system drive letters
  861. while (!fFound)
  862. {
  863. iLen = lstrlen(pwcTempDriveString);
  864. if (iLen == 0)
  865. break;
  866. pwcTempDriveString[0] = towupper(pwcTempDriveString[0]);
  867. if (pwcTempDriveString[0] == wcDriveLetter)
  868. {
  869. fFound = TRUE;
  870. break;
  871. }
  872. pwcTempDriveString = &pwcTempDriveString [iLen + 1];
  873. }
  874. }
  875. }
  876. return !fFound;
  877. }
  878. BOOL
  879. IsDriveLetterSticky(
  880. IN WCHAR* pwszDriveLetter
  881. )
  882. {
  883. BOOL fFound = FALSE;
  884. WCHAR wszTempVolumeName [MAX_PATH+1];
  885. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsDriveLetterSticky");
  886. WCHAR wszDrivePath[g_cchDriveName];
  887. _ASSERTE(pwszDriveLetter != NULL);
  888. wszDrivePath[0] = towupper(pwszDriveLetter[0]);
  889. wszDrivePath[1] = L':';
  890. wszDrivePath[2] = L'\\';
  891. wszDrivePath[3] = L'\0';
  892. if (GetVolumeNameForVolumeMountPoint(
  893. wszDrivePath,
  894. wszTempVolumeName,
  895. MAX_PATH))
  896. {
  897. WCHAR wszCurrentDrivePath[g_cchDriveName];
  898. if (GetVolumeDrive(wszTempVolumeName, g_cchDriveName, wszCurrentDrivePath))
  899. {
  900. wszCurrentDrivePath[0] = towupper(wszCurrentDrivePath[0]);
  901. if (wszDrivePath[0] == wszCurrentDrivePath[0])
  902. fFound = TRUE;
  903. }
  904. }
  905. return fFound;
  906. }
  907. BOOL
  908. IsBootDrive(
  909. IN WCHAR* pwszDriveLetter
  910. )
  911. {
  912. WCHAR wszSystemDirectory[MAX_PATH+1];
  913. CVssFunctionTracer ft(VSSDBG_VSSADMIN, L"IsBootDrive");
  914. _ASSERTE(pwszDriveLetter != NULL);
  915. WCHAR wcDrive = towupper(pwszDriveLetter[0]);
  916. if (!GetSystemDirectory(wszSystemDirectory, MAX_PATH+1))
  917. {
  918. DWORD dwErr = GetLastError();
  919. ft.hr = HRESULT_FROM_WIN32(dwErr);
  920. ft.Throw(VSSDBG_VSSADMIN, ft.hr, L"GetSystemDirectory failed %#x", dwErr);
  921. }
  922. wszSystemDirectory[0] = towupper(wszSystemDirectory[0]);
  923. if (wcDrive == wszSystemDirectory[0])
  924. return TRUE;
  925. return FALSE;
  926. }
  927. BOOL
  928. DeleteNetworkShare(
  929. IN WCHAR* pwszDriveRoot
  930. )
  931. {
  932. NET_API_STATUS status;
  933. WCHAR wszShareName[3];
  934. wszShareName[0] = pwszDriveRoot[0];
  935. wszShareName[1] = L'$';
  936. wszShareName[2] = L'\0';
  937. status = NetShareDel(NULL, wszShareName, 0);
  938. if ( status!=NERR_Success )
  939. return FALSE;
  940. else
  941. return TRUE;
  942. }