VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/HBDMgmt-win.cpp@ 77807

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: HBDMgmt-win.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBox storage devices: Host block device management API.
4 */
5
6/*
7 * Copyright (C) 2015-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOG_GROUP LOG_GROUP_DRV_VD
18#include <VBox/cdefs.h>
19#include <VBox/err.h>
20#include <VBox/log.h>
21#include <iprt/assert.h>
22#include <iprt/string.h>
23#include <iprt/mem.h>
24#include <iprt/semaphore.h>
25#include <iprt/list.h>
26
27#include <iprt/nt/nt-and-windows.h>
28#include <iprt/win/windows.h>
29
30#include "HBDMgmt.h"
31
32
33/*********************************************************************************************************************************
34* Defined Constants And Macros *
35*********************************************************************************************************************************/
36
37
38/*********************************************************************************************************************************
39* Structures and Typedefs *
40*********************************************************************************************************************************/
41
42/**
43 * Claimed block device state.
44 */
45typedef struct HBDMGRDEV
46{
47 /** List node. */
48 RTLISTNODE ListNode;
49 /** The block device name. */
50 char *pszDevice;
51 /** Number of volumes for this block device. */
52 unsigned cVolumes;
53 /** Array of handle to the volumes for unmounting and taking it offline. */
54 HANDLE ahVolumes[1];
55} HBDMGRDEV;
56/** Pointer to a claimed block device. */
57typedef HBDMGRDEV *PHBDMGRDEV;
58
59/**
60 * Internal Host block device manager state.
61 */
62typedef struct HBDMGRINT
63{
64 /** List of claimed block devices. */
65 RTLISTANCHOR ListClaimed;
66 /** Fast mutex protecting the list. */
67 RTSEMFASTMUTEX hMtxList;
68} HBDMGRINT;
69/** Pointer to an interal block device manager state. */
70typedef HBDMGRINT *PHBDMGRINT;
71
72#define HBDMGR_NT_HARDDISK_START "\\Device\\Harddisk"
73
74
75/*********************************************************************************************************************************
76* Global Variables *
77*********************************************************************************************************************************/
78
79
80/*********************************************************************************************************************************
81* Internal Functions *
82*********************************************************************************************************************************/
83
84/**
85 * Unclaims the given block device and frees its state removing it from the list.
86 *
87 * @returns nothing.
88 * @param pDev The block device to unclaim.
89 */
90static void hbdMgrDevUnclaim(PHBDMGRDEV pDev)
91{
92 LogFlowFunc(("pDev=%p{%s} cVolumes=%u\n", pDev, pDev->pszDevice, pDev->cVolumes));
93
94 for (unsigned i = 0; i < pDev->cVolumes; i++)
95 {
96 DWORD dwReturned = 0;
97
98 LogFlowFunc(("Taking volume %u online\n", i));
99 BOOL bRet = DeviceIoControl(pDev->ahVolumes[i], IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, &dwReturned, NULL);
100 if (!bRet)
101 LogRel(("HBDMgmt: Failed to take claimed volume online during cleanup: %s{%Rrc}\n",
102 pDev->pszDevice, RTErrConvertFromWin32(GetLastError())));
103
104 CloseHandle(pDev->ahVolumes[i]);
105 }
106
107 RTListNodeRemove(&pDev->ListNode);
108 RTStrFree(pDev->pszDevice);
109 RTMemFree(pDev);
110}
111
112/**
113 * Returns the block device given by the filename if claimed or NULL.
114 *
115 * @returns Pointer to the claimed block device or NULL if not claimed.
116 * @param pThis The block device manager.
117 * @param pszFilename The name to look for.
118 */
119static PHBDMGRDEV hbdMgrDevFindByName(PHBDMGRINT pThis, const char *pszFilename)
120{
121 bool fFound = false;
122
123 PHBDMGRDEV pIt;
124 RTListForEach(&pThis->ListClaimed, pIt, HBDMGRDEV, ListNode)
125 {
126 if (!RTStrCmp(pszFilename, pIt->pszDevice))
127 {
128 fFound = true;
129 break;
130 }
131 }
132
133 return fFound ? pIt : NULL;
134}
135
136/**
137 * Queries the target in the NT namespace of the given symbolic link.
138 *
139 * @returns VBox status code.
140 * @param pwszLinkNt The symbolic link to query the target for.
141 * @param ppwszLinkTarget Where to store the link target in the NT namespace on success.
142 * Must be freed with RTUtf16Free().
143 */
144static int hbdMgrQueryNtLinkTarget(PRTUTF16 pwszLinkNt, PRTUTF16 *ppwszLinkTarget)
145{
146 int rc = VINF_SUCCESS;
147 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
148 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
149 UNICODE_STRING NtName;
150
151 NtName.Buffer = (PWSTR)pwszLinkNt;
152 NtName.Length = (USHORT)(RTUtf16Len(pwszLinkNt) * sizeof(RTUTF16));
153 NtName.MaximumLength = NtName.Length + sizeof(RTUTF16);
154
155 OBJECT_ATTRIBUTES ObjAttr;
156 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
157
158 NTSTATUS rcNt = NtOpenSymbolicLinkObject(&hFile, SYMBOLIC_LINK_QUERY, &ObjAttr);
159 if (NT_SUCCESS(rcNt))
160 {
161 UNICODE_STRING UniStr;
162 RTUTF16 awszBuf[1024];
163 RT_ZERO(awszBuf);
164 UniStr.Buffer = awszBuf;
165 UniStr.MaximumLength = sizeof(awszBuf);
166 rcNt = NtQuerySymbolicLinkObject(hFile, &UniStr, NULL);
167 if (NT_SUCCESS(rcNt))
168 {
169 *ppwszLinkTarget = RTUtf16Dup((PRTUTF16)UniStr.Buffer);
170 if (!*ppwszLinkTarget)
171 rc = VERR_NO_STR_MEMORY;
172 }
173 else
174 rc = RTErrConvertFromNtStatus(rcNt);
175
176 CloseHandle(hFile);
177 }
178 else
179 rc = RTErrConvertFromNtStatus(rcNt);
180
181 return rc;
182}
183
184/**
185 * Queries the harddisk volume device in the NT namespace for the given Win32
186 * block device path.
187 *
188 * @returns VBox status code.
189 * @param pwszDriveWin32 The Win32 path to the block device (e.g. "\\.\PhysicalDrive0" for example)
190 * @param ppwszDriveNt Where to store the NT path to the volume on success.
191 * Must be freed with RTUtf16Free().
192 */
193static int hbdMgrQueryNtName(PRTUTF16 pwszDriveWin32, PRTUTF16 *ppwszDriveNt)
194{
195 int rc = VINF_SUCCESS;
196 RTUTF16 awszFileNt[1024];
197
198 /*
199 * Make sure the path is at least 5 characters long so we can safely access
200 * the part following \\.\ below.
201 */
202 AssertReturn(RTUtf16Len(pwszDriveWin32) >= 5, VERR_INVALID_STATE);
203
204 RT_ZERO(awszFileNt);
205 RTUtf16CatAscii(awszFileNt, RT_ELEMENTS(awszFileNt), "\\??\\");
206 RTUtf16Cat(awszFileNt, RT_ELEMENTS(awszFileNt), &pwszDriveWin32[4]);
207
208 /* Make sure there is no trailing \ at the end or we will fail. */
209 size_t cwcPath = RTUtf16Len(awszFileNt);
210 if (awszFileNt[cwcPath - 1] == L'\\')
211 awszFileNt[cwcPath - 1] = L'\0';
212
213 return hbdMgrQueryNtLinkTarget(awszFileNt, ppwszDriveNt);
214}
215
216static int hbdMgrQueryAllMountpointsForDisk(PRTUTF16 pwszDiskNt, PRTUTF16 **ppapwszVolumes,
217 unsigned *pcVolumes)
218{
219 /*
220 * Try to get the symlink target for every partition, we will take the easy approach
221 * and just try to open every partition starting with \Device\Harddisk<N>\Partition1
222 * (0 is a special reserved partition linking to the complete disk).
223 *
224 * For every partition we get the target \Device\HarddiskVolume<N> and query all mountpoints
225 * with that.
226 */
227 int rc = VINF_SUCCESS;
228 char *pszDiskNt = NULL;
229 unsigned cVolumes = 0;
230 unsigned cVolumesMax = 10;
231 PRTUTF16 *papwszVolumes = (PRTUTF16 *)RTMemAllocZ(cVolumesMax * sizeof(PRTUTF16));
232
233 if (!papwszVolumes)
234 return VERR_NO_MEMORY;
235
236 rc = RTUtf16ToUtf8(pwszDiskNt, &pszDiskNt);
237 if (RT_SUCCESS(rc))
238 {
239 /* Check that the path matches our expectation \Device\Harddisk<N>\DR<N>. */
240 if (!RTStrNCmp(pszDiskNt, HBDMGR_NT_HARDDISK_START, sizeof(HBDMGR_NT_HARDDISK_START) - 1))
241 {
242 uint32_t iDisk = 0;
243
244 rc = RTStrToUInt32Ex(pszDiskNt + sizeof(HBDMGR_NT_HARDDISK_START) - 1, NULL, 10, &iDisk);
245 if (RT_SUCCESS(rc) || rc == VWRN_TRAILING_CHARS)
246 {
247 uint32_t iPart = 1;
248
249 /* Try to query all mount points for all partitions, the simple way. */
250 do
251 {
252 char aszDisk[1024];
253 RT_ZERO(aszDisk);
254
255 size_t cchWritten = RTStrPrintf(&aszDisk[0], sizeof(aszDisk), "\\Device\\Harddisk%u\\Partition%u", iDisk, iPart);
256 if (cchWritten < sizeof(aszDisk))
257 {
258 PRTUTF16 pwszDisk = NULL;
259 rc = RTStrToUtf16(&aszDisk[0], &pwszDisk);
260 if (RT_SUCCESS(rc))
261 {
262 PRTUTF16 pwszTargetNt = NULL;
263
264 rc = hbdMgrQueryNtLinkTarget(pwszDisk, &pwszTargetNt);
265 if (RT_SUCCESS(rc))
266 {
267 if (cVolumes == cVolumesMax)
268 {
269 /* Increase array of volumes. */
270 PRTUTF16 *papwszVolumesNew = (PRTUTF16 *)RTMemAllocZ((cVolumesMax + 10) * sizeof(PRTUTF16));
271 if (papwszVolumesNew)
272 {
273 cVolumesMax += 10;
274 papwszVolumes = papwszVolumesNew;
275 }
276 else
277 {
278 RTUtf16Free(pwszTargetNt);
279 rc = VERR_NO_MEMORY;
280 }
281 }
282
283 if (RT_SUCCESS(rc))
284 {
285 Assert(cVolumes < cVolumesMax);
286 papwszVolumes[cVolumes++] = pwszTargetNt;
287 iPart++;
288 }
289 }
290 else if (rc == VERR_FILE_NOT_FOUND)
291 {
292 /* The partition does not exist, so stop trying. */
293 rc = VINF_SUCCESS;
294 break;
295 }
296
297 RTUtf16Free(pwszDisk);
298 }
299 }
300 else
301 rc = VERR_BUFFER_OVERFLOW;
302
303 } while (RT_SUCCESS(rc));
304 }
305 }
306 else
307 rc = VERR_INVALID_STATE;
308
309 RTStrFree(pszDiskNt);
310 }
311
312 if (RT_SUCCESS(rc))
313 {
314 *pcVolumes = cVolumes;
315 *ppapwszVolumes = papwszVolumes;
316 LogFlowFunc(("rc=%Rrc cVolumes=%u ppapwszVolumes=%p\n", rc, cVolumes, papwszVolumes));
317 }
318 else
319 {
320 for (unsigned i = 0; i < cVolumes; i++)
321 RTUtf16Free(papwszVolumes[i]);
322
323 RTMemFree(papwszVolumes);
324 }
325
326 return rc;
327}
328
329static NTSTATUS hbdMgrNtCreateFileWrapper(PRTUTF16 pwszVolume, HANDLE *phVolume)
330{
331 HANDLE hVolume = RTNT_INVALID_HANDLE_VALUE;
332 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
333 UNICODE_STRING NtName;
334
335 NtName.Buffer = (PWSTR)pwszVolume;
336 NtName.Length = (USHORT)(RTUtf16Len(pwszVolume) * sizeof(RTUTF16));
337 NtName.MaximumLength = NtName.Length + sizeof(WCHAR);
338
339 OBJECT_ATTRIBUTES ObjAttr;
340 InitializeObjectAttributes(&ObjAttr, &NtName, OBJ_CASE_INSENSITIVE, NULL /*hRootDir*/, NULL /*pSecDesc*/);
341
342 NTSTATUS rcNt = NtCreateFile(&hVolume,
343 FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE,
344 &ObjAttr,
345 &Ios,
346 NULL /* Allocation Size*/,
347 FILE_ATTRIBUTE_NORMAL,
348 FILE_SHARE_READ | FILE_SHARE_WRITE,
349 FILE_OPEN,
350 FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
351 NULL /*EaBuffer*/,
352 0 /*EaLength*/);
353 if (NT_SUCCESS(rcNt))
354 rcNt = Ios.Status;
355
356 if (NT_SUCCESS(rcNt))
357 *phVolume = hVolume;
358
359 return rcNt;
360}
361
362DECLHIDDEN(int) HBDMgrCreate(PHBDMGR phHbdMgr)
363{
364 AssertPtrReturn(phHbdMgr, VERR_INVALID_POINTER);
365
366 PHBDMGRINT pThis = (PHBDMGRINT)RTMemAllocZ(sizeof(HBDMGRINT));
367 if (RT_UNLIKELY(!pThis))
368 return VERR_NO_MEMORY;
369
370 int rc = VINF_SUCCESS;
371 RTListInit(&pThis->ListClaimed);
372
373 rc = RTSemFastMutexCreate(&pThis->hMtxList);
374 if (RT_SUCCESS(rc))
375 {
376 *phHbdMgr = pThis;
377 return VINF_SUCCESS;
378 }
379
380 RTMemFree(pThis);
381 return rc;
382}
383
384DECLHIDDEN(void) HBDMgrDestroy(HBDMGR hHbdMgr)
385{
386 PHBDMGRINT pThis = hHbdMgr;
387 AssertPtrReturnVoid(pThis);
388
389 /* Go through all claimed block devices and release them. */
390 RTSemFastMutexRequest(pThis->hMtxList);
391 PHBDMGRDEV pIt, pItNext;
392 RTListForEachSafe(&pThis->ListClaimed, pIt, pItNext, HBDMGRDEV, ListNode)
393 {
394 hbdMgrDevUnclaim(pIt);
395 }
396 RTSemFastMutexRelease(pThis->hMtxList);
397
398 RTSemFastMutexDestroy(pThis->hMtxList);
399 RTMemFree(pThis);
400}
401
402DECLHIDDEN(bool) HBDMgrIsBlockDevice(const char *pszFilename)
403{
404 bool fIsBlockDevice = RTStrNICmp(pszFilename, "\\\\.\\PhysicalDrive", sizeof("\\\\.\\PhysicalDrive") - 1) == 0 ? true : false;
405 if (!fIsBlockDevice)
406 fIsBlockDevice = RTStrNICmp(pszFilename, "\\\\.\\Harddisk", sizeof("\\\\.\\Harddisk") - 1) == 0 ? true : false;
407
408 LogFlowFunc(("returns %s -> %RTbool\n", pszFilename, fIsBlockDevice));
409 return fIsBlockDevice;
410}
411
412DECLHIDDEN(int) HBDMgrClaimBlockDevice(HBDMGR hHbdMgr, const char *pszFilename)
413{
414 PHBDMGRINT pThis = hHbdMgr;
415 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
416 AssertReturn(HBDMgrIsBlockDevice(pszFilename), VERR_INVALID_PARAMETER);
417
418 int rc = VINF_SUCCESS;
419 PHBDMGRDEV pDev = hbdMgrDevFindByName(pThis, pszFilename);
420 if (!pDev)
421 {
422 PRTUTF16 pwszVolume = NULL;
423
424 rc = RTStrToUtf16(pszFilename, &pwszVolume);
425 if (RT_SUCCESS(rc))
426 {
427 PRTUTF16 pwszVolNt = NULL;
428
429 rc = hbdMgrQueryNtName(pwszVolume, &pwszVolNt);
430 if (RT_SUCCESS(rc))
431 {
432 PRTUTF16 *papwszVolumes = NULL;
433 unsigned cVolumes = 0;
434
435 /* Complete disks need to be handled differently. */
436 if (!RTStrNCmp(pszFilename, "\\\\.\\PhysicalDrive", sizeof("\\\\.\\PhysicalDrive") - 1))
437 {
438 rc = hbdMgrQueryAllMountpointsForDisk(pwszVolNt, &papwszVolumes, &cVolumes);
439 RTUtf16Free(pwszVolNt);
440 }
441 else
442 {
443 papwszVolumes = &pwszVolNt;
444 cVolumes = 1;
445 }
446
447 if (RT_SUCCESS(rc))
448 {
449#ifdef LOG_ENABLED
450 for (unsigned i = 0; i < cVolumes; i++)
451 LogFlowFunc(("Volume %u: %ls\n", i, papwszVolumes[i]));
452#endif
453 pDev = (PHBDMGRDEV)RTMemAllocZ(RT_UOFFSETOF_DYN(HBDMGRDEV, ahVolumes[cVolumes]));
454 if (pDev)
455 {
456 pDev->cVolumes = 0;
457 pDev->pszDevice = RTStrDup(pszFilename);
458 if (pDev->pszDevice)
459 {
460 for (unsigned i = 0; i < cVolumes; i++)
461 {
462 HANDLE hVolume;
463
464 NTSTATUS rcNt = hbdMgrNtCreateFileWrapper(papwszVolumes[i], &hVolume);
465 if (NT_SUCCESS(rcNt))
466 {
467 DWORD dwReturned = 0;
468
469 Assert(hVolume != INVALID_HANDLE_VALUE);
470 BOOL bRet = DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwReturned, NULL);
471 if (bRet)
472 {
473 bRet = DeviceIoControl(hVolume, IOCTL_VOLUME_OFFLINE, NULL, 0, NULL, 0, &dwReturned, NULL);
474 if (bRet)
475 pDev->ahVolumes[pDev->cVolumes++] = hVolume;
476 else
477 rc = RTErrConvertFromWin32(GetLastError());
478 }
479 else
480 rc = RTErrConvertFromWin32(GetLastError());
481
482 if (RT_FAILURE(rc))
483 CloseHandle(hVolume);
484 }
485 else
486 rc = RTErrConvertFromNtStatus(rcNt);
487 }
488 }
489 else
490 rc = VERR_NO_STR_MEMORY;
491
492 if (RT_SUCCESS(rc))
493 {
494 RTSemFastMutexRequest(pThis->hMtxList);
495 RTListAppend(&pThis->ListClaimed, &pDev->ListNode);
496 RTSemFastMutexRelease(pThis->hMtxList);
497 }
498 else
499 {
500 /* Close all open handles and take the volumes online again. */
501 for (unsigned i = 0; i < pDev->cVolumes; i++)
502 {
503 DWORD dwReturned = 0;
504 BOOL bRet = DeviceIoControl(pDev->ahVolumes[i], IOCTL_VOLUME_ONLINE, NULL, 0, NULL, 0, &dwReturned, NULL);
505 if (!bRet)
506 LogRel(("HBDMgmt: Failed to take claimed volume online during cleanup: %s{%Rrc}\n",
507 pDev->pszDevice, RTErrConvertFromWin32(GetLastError())));
508
509 CloseHandle(pDev->ahVolumes[i]);
510 }
511 if (pDev->pszDevice)
512 RTStrFree(pDev->pszDevice);
513 RTMemFree(pDev);
514 }
515 }
516 else
517 rc = VERR_NO_MEMORY;
518
519 for (unsigned i = 0; i < cVolumes; i++)
520 RTUtf16Free(papwszVolumes[i]);
521
522 RTMemFree(papwszVolumes);
523 }
524 }
525
526 RTUtf16Free(pwszVolume);
527 }
528 }
529 else
530 rc = VERR_ALREADY_EXISTS;
531
532 return rc;
533}
534
535DECLHIDDEN(int) HBDMgrUnclaimBlockDevice(HBDMGR hHbdMgr, const char *pszFilename)
536{
537 PHBDMGRINT pThis = hHbdMgr;
538 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
539
540 RTSemFastMutexRequest(pThis->hMtxList);
541 int rc = VINF_SUCCESS;
542 PHBDMGRDEV pDev = hbdMgrDevFindByName(pThis, pszFilename);
543 if (pDev)
544 hbdMgrDevUnclaim(pDev);
545 else
546 rc = VERR_NOT_FOUND;
547 RTSemFastMutexRelease(pThis->hMtxList);
548
549 return rc;
550}
551
552DECLHIDDEN(bool) HBDMgrIsBlockDeviceClaimed(HBDMGR hHbdMgr, const char *pszFilename)
553{
554 PHBDMGRINT pThis = hHbdMgr;
555 AssertPtrReturn(pThis, false);
556
557 RTSemFastMutexRequest(pThis->hMtxList);
558 PHBDMGRDEV pIt = hbdMgrDevFindByName(pThis, pszFilename);
559 RTSemFastMutexRelease(pThis->hMtxList);
560
561 return pIt ? true : false;
562}
563
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette