VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/HBDMgmt-darwin.cpp@ 57358

Last change on this file since 57358 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.4 KB
Line 
1/* $Id: HBDMgmt-darwin.cpp 57358 2015-08-14 15:16:38Z vboxsync $ */
2/** @file
3 * VBox storage devices: Host block device management API - darwin specifics.
4 */
5
6/*
7 * Copyright (C) 2015 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_VD
23#include <VBox/cdefs.h>
24#include <VBox/err.h>
25#include <iprt/assert.h>
26#include <iprt/list.h>
27#include <iprt/mem.h>
28#include <iprt/string.h>
29#include <iprt/once.h>
30#include <iprt/semaphore.h>
31#include <iprt/path.h>
32#include <iprt/thread.h>
33
34#include <DiskArbitration/DiskArbitration.h>
35
36#include "HBDMgmt.h"
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42
43
44/*********************************************************************************************************************************
45* Structures and Typedefs *
46*********************************************************************************************************************************/
47
48/**
49 * Claimed block device state.
50 */
51typedef struct HBDMGRDEV
52{
53 /** List node. */
54 RTLISTNODE ListNode;
55 /** Handle to the DA Disk object. */
56 DADiskRef hDiskRef;
57} HBDMGRDEV;
58/** Pointer to a claimed block device. */
59typedef HBDMGRDEV *PHBDMGRDEV;
60
61/**
62 * Internal Host block device manager state.
63 */
64typedef struct HBDMGRINT
65{
66 /** Session handle to the DiskArbitration daemon. */
67 DASessionRef hSessionRef;
68 /** Runloop reference of the worker thread. */
69 CFRunLoopRef hRunLoopRef;
70 /** Runloop source for waking up the worker thread. */
71 CFRunLoopSourceRef hRunLoopSrcWakeRef;
72 /** List of claimed block devices. */
73 RTLISTANCHOR ListClaimed;
74 /** Fast mutex protecting the list. */
75 RTSEMFASTMUTEX hMtxList;
76 /** Event sempahore to signal callback completion. */
77 RTSEMEVENT hEvtCallback;
78 /** Thread processing DA events. */
79 RTTHREAD hThrdDAEvts;
80 /** Flag whether the thread should keep running. */
81 volatile bool fRunning;
82} HBDMGRINT;
83/** Pointer to an interal block device manager state. */
84typedef HBDMGRINT *PHBDMGRINT;
85
86/**
87 * Helper structure containing the arguments
88 * for the claim/unmount callbacks.
89 */
90typedef struct HBDMGRDACLBKARGS
91{
92 /** Pointer to the block device manager. */
93 PHBDMGRINT pThis;
94 /** The status code returned by the callback, after the operation completed. */
95 DAReturn rcDA;
96} HBDMGRDACLBKARGS;
97typedef HBDMGRDACLBKARGS *PHBDMGRDACLBKARGS;
98
99
100/*********************************************************************************************************************************
101* Global Variables *
102*********************************************************************************************************************************/
103
104
105/*********************************************************************************************************************************
106* Internal Functions *
107*********************************************************************************************************************************/
108
109/**
110 * Unclaims the given block device and frees its state removing it from the list.
111 *
112 * @returns nothing.
113 * @param pDev The block device to unclaim.
114 */
115static void hbdMgrDevUnclaim(PHBDMGRDEV pDev)
116{
117 DADiskUnclaim(pDev->hDiskRef);
118 CFRelease(pDev->hDiskRef);
119 RTListNodeRemove(&pDev->ListNode);
120 RTMemFree(pDev);
121}
122
123/**
124 * Returns the block device given by the filename if claimed or NULL.
125 *
126 * @returns Pointer to the claimed block device or NULL if not claimed.
127 * @param pThis The block device manager.
128 * @param pszFilename The name to look for.
129 */
130static PHBDMGRDEV hbdMgrDevFindByName(PHBDMGRINT pThis, const char *pszFilename)
131{
132 bool fFound = false;
133 const char *pszFilenameStripped = RTPathFilename(pszFilename);
134
135 AssertPtrReturn(pszFilenameStripped, NULL);
136
137 PHBDMGRDEV pIt;
138 RTListForEach(&pThis->ListClaimed, pIt, HBDMGRDEV, ListNode)
139 {
140 const char *pszBSDName = DADiskGetBSDName(pIt->hDiskRef);
141 if (!RTStrCmp(pszFilenameStripped, pszBSDName))
142 {
143 fFound = true;
144 break;
145 }
146 }
147
148 return fFound ? pIt : NULL;
149}
150
151/**
152 * Converts a given DA return code to a VBox status code.
153 *
154 * @returns VBox status code.
155 * @param hReturn The status code returned by a DA API call.
156 */
157static int hbdMgrDAReturn2VBoxStatus(DAReturn hReturn)
158{
159 int rc = VERR_UNRESOLVED_ERROR;
160
161 switch (hReturn)
162 {
163 case kDAReturnBusy:
164 rc = VERR_RESOURCE_BUSY;
165 break;
166 case kDAReturnNotMounted:
167 case kDAReturnBadArgument:
168 rc = VERR_INVALID_PARAMETER;
169 break;
170 case kDAReturnNotPermitted:
171 case kDAReturnNotPrivileged:
172 case kDAReturnExclusiveAccess:
173 rc = VERR_ACCESS_DENIED;
174 break;
175 case kDAReturnNoResources:
176 rc = VERR_NO_MEMORY;
177 break;
178 case kDAReturnNotFound:
179 rc = VERR_NOT_FOUND;
180 break;
181 case kDAReturnNotReady:
182 rc = VERR_TRY_AGAIN;
183 break;
184 case kDAReturnNotWritable:
185 rc = VERR_WRITE_PROTECT;
186 break;
187 case kDAReturnUnsupported:
188 rc = VERR_NOT_SUPPORTED;
189 break;
190 case kDAReturnError:
191 default:
192 rc = VERR_UNRESOLVED_ERROR;
193 }
194
195 return rc;
196}
197
198/**
199 * Callback notifying us that the async DADiskClaim()/DADiskUnmount call has completed.
200 *
201 * @param hDiskRef The disk that was attempted claimed / unmounted.
202 * @param hDissenterRef NULL on success, contains details on failure.
203 * @param pvContext Pointer to the return code variable.
204 */
205static DECLCALLBACK(void) hbdMgrDACallbackComplete(DADiskRef hDiskRef, DADissenterRef hDissenterRef, void *pvContext)
206{
207 PHBDMGRDACLBKARGS pArgs = (PHBDMGRDACLBKARGS)pvContext;
208 if (!hDissenterRef)
209 pArgs->rcDA = kDAReturnSuccess;
210 else
211 pArgs->rcDA = DADissenterGetStatus(hDissenterRef);
212 RTSemEventSignal(pArgs->pThis->hEvtCallback);
213}
214
215/**
216 * Callback notifying us about any attempt to mount a volume. If we claimed the volume
217 * or the complete disk containing the volume we will deny the attempt.
218 *
219 * @returns Reference to a DADissenter object which contains the result.
220 * @param hDiskRef The disk that is about to be mounted.
221 * @param pvCOntext Pointer to the block device manager.
222 */
223static DECLCALLBACK(DADissenterRef) hbdMgrDAMountApprovalCallback(DADiskRef hDiskRef, void *pvContext)
224{
225 PHBDMGRINT pThis = (PHBDMGRINT)pvContext;
226 DADiskRef hDiskParentRef = DADiskCopyWholeDisk(hDiskRef);
227 const char *pszBSDName = DADiskGetBSDName(hDiskRef);
228 const char *pszBSDNameParent = hDiskParentRef ? DADiskGetBSDName(hDiskParentRef) : NULL;
229 DADissenterRef hDissenterRef = NULL;
230
231 RTSemFastMutexRequest(pThis->hMtxList);
232 PHBDMGRDEV pIt;
233 RTListForEach(&pThis->ListClaimed, pIt, HBDMGRDEV, ListNode)
234 {
235 const char *pszBSDNameCur = DADiskGetBSDName(pIt->hDiskRef);
236 /*
237 * Prevent mounting any volume we have in use. This applies to the case
238 * where we have the whole disk occupied but a single volume is about to be
239 * mounted.
240 */
241 if ( !RTStrCmp(pszBSDNameCur, pszBSDName)
242 || ( pszBSDNameParent
243 && !RTStrCmp(pszBSDNameParent, pszBSDNameCur)))
244 {
245 CFStringRef hStrReason = CFStringCreateWithCString(kCFAllocatorDefault, "The disk is currently in use by VirtualBox and cannot be mounted", kCFStringEncodingUTF8);
246 hDissenterRef = DADissenterCreate(kCFAllocatorDefault, kDAReturnExclusiveAccess, hStrReason);
247 break;
248 }
249 }
250
251 RTSemFastMutexRelease(pThis->hMtxList);
252
253 if (hDiskParentRef)
254 CFRelease(hDiskParentRef);
255 return hDissenterRef;
256}
257
258
259/**
260 * Dummy handler for the wakeup source to kick the worker thread.
261 *
262 * @returns nothing.
263 * @param pInfo Opaque user data given during source creation, unused.
264 */
265static DECLCALLBACK(void) hbdMgrDAPerformWakeup(void *pInfo)
266{
267 return;
268}
269
270
271/**
272 * Worker function of the thread processing messages from the Disk Arbitration daemon.
273 *
274 * @returns IPRT status code.
275 * @param hThreadSelf The thread handle.
276 * @param pvUser Opaque user data, the block device manager instance.
277 */
278static DECLCALLBACK(int) hbdMgrDAWorker(RTTHREAD hThreadSelf, void *pvUser)
279{
280 PHBDMGRINT pThis = (PHBDMGRINT)pvUser;
281
282 /* Provide the runloop reference. */
283 pThis->hRunLoopRef = CFRunLoopGetCurrent();
284 RTThreadUserSignal(hThreadSelf);
285
286 /* Add the wake source to our runloop so we get notified about state changes. */
287 CFRunLoopAddSource(pThis->hRunLoopRef, pThis->hRunLoopSrcWakeRef, kCFRunLoopCommonModes);
288
289 /* Do what we are here for. */
290 while (ASMAtomicReadBool(&pThis->fRunning))
291 {
292 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10.0, true);
293 }
294
295 /* Remove the wakeup source form our runloop. */
296 CFRunLoopRemoveSource(pThis->hRunLoopRef, pThis->hRunLoopSrcWakeRef, kCFRunLoopCommonModes);
297
298 return VINF_SUCCESS;
299}
300
301DECLHIDDEN(int) HBDMgrCreate(PHBDMGR phHbdMgr)
302{
303 AssertPtrReturn(phHbdMgr, VERR_INVALID_POINTER);
304
305 PHBDMGRINT pThis = (PHBDMGRINT)RTMemAllocZ(sizeof(HBDMGRINT));
306 if (RT_UNLIKELY(!pThis))
307 return VERR_NO_MEMORY;
308
309 int rc = VINF_SUCCESS;
310 RTListInit(&pThis->ListClaimed);
311 pThis->fRunning = true;
312 pThis->hSessionRef = DASessionCreate(kCFAllocatorDefault);
313 if (pThis->hSessionRef)
314 {
315 rc = RTSemFastMutexCreate(&pThis->hMtxList);
316 if (RT_SUCCESS(rc))
317 {
318 rc = RTSemEventCreate(&pThis->hEvtCallback);
319 if (RT_SUCCESS(rc))
320 {
321 CFRunLoopSourceContext CtxRunLoopSource;
322 CtxRunLoopSource.version = 0;
323 CtxRunLoopSource.info = NULL;
324 CtxRunLoopSource.retain = NULL;
325 CtxRunLoopSource.release = NULL;
326 CtxRunLoopSource.copyDescription = NULL;
327 CtxRunLoopSource.equal = NULL;
328 CtxRunLoopSource.hash = NULL;
329 CtxRunLoopSource.schedule = NULL;
330 CtxRunLoopSource.cancel = NULL;
331 CtxRunLoopSource.perform = hbdMgrDAPerformWakeup;
332 pThis->hRunLoopSrcWakeRef = CFRunLoopSourceCreate(NULL, 0, &CtxRunLoopSource);
333 if (CFRunLoopSourceIsValid(pThis->hRunLoopSrcWakeRef))
334 {
335 rc = RTThreadCreate(&pThis->hThrdDAEvts, hbdMgrDAWorker, pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "HbdDA-Wrk");
336 if (RT_SUCCESS(rc))
337 {
338 /* Wait for the thread to start up and provide the runloop reference. */
339 rc = RTThreadUserWait(pThis->hThrdDAEvts, RT_INDEFINITE_WAIT);
340 AssertRC(rc);
341 AssertPtr(pThis->hRunLoopRef);
342
343 DARegisterDiskMountApprovalCallback(pThis->hSessionRef, NULL, hbdMgrDAMountApprovalCallback, pThis);
344 DASessionScheduleWithRunLoop(pThis->hSessionRef, pThis->hRunLoopRef, kCFRunLoopDefaultMode);
345 *phHbdMgr = pThis;
346 return VINF_SUCCESS;
347 }
348 CFRelease(pThis->hRunLoopSrcWakeRef);
349 }
350 }
351
352 RTSemFastMutexDestroy(pThis->hMtxList);
353 }
354
355 CFRelease(pThis->hSessionRef);
356 }
357 else
358 rc = VERR_NO_MEMORY;
359
360 RTMemFree(pThis);
361 return rc;
362}
363
364
365DECLHIDDEN(void) HBDMgrDestroy(HBDMGR hHbdMgr)
366{
367 PHBDMGRINT pThis = hHbdMgr;
368 AssertPtrReturnVoid(pThis);
369
370 /* Unregister the mount approval and DA session from the runloop. */
371 DASessionUnscheduleFromRunLoop(pThis->hSessionRef, pThis->hRunLoopRef, kCFRunLoopDefaultMode);
372 DAUnregisterApprovalCallback(pThis->hSessionRef, (void *)hbdMgrDAMountApprovalCallback, pThis);
373
374 /* Kick the worker thread to exit. */
375 ASMAtomicXchgBool(&pThis->fRunning, false);
376 CFRunLoopSourceSignal(pThis->hRunLoopSrcWakeRef);
377 CFRunLoopWakeUp(pThis->hRunLoopRef);
378 int rcThrd = VINF_SUCCESS;
379 int rc = RTThreadWait(pThis->hThrdDAEvts, RT_INDEFINITE_WAIT, &rcThrd);
380 AssertRC(rc); AssertRC(rcThrd);
381
382 CFRelease(pThis->hRunLoopSrcWakeRef);
383
384 /* Go through all claimed block devices and release them. */
385 RTSemFastMutexRequest(pThis->hMtxList);
386 PHBDMGRDEV pIt, pItNext;
387 RTListForEachSafe(&pThis->ListClaimed, pIt, pItNext, HBDMGRDEV, ListNode)
388 {
389 hbdMgrDevUnclaim(pIt);
390 }
391 RTSemFastMutexRelease(pThis->hMtxList);
392
393 CFRelease(pThis->hSessionRef);
394 RTSemFastMutexDestroy(pThis->hMtxList);
395 RTSemEventDestroy(pThis->hEvtCallback);
396 RTMemFree(pThis);
397}
398
399
400DECLHIDDEN(bool) HBDMgrIsBlockDevice(const char *pszFilename)
401{
402 bool fIsBlockDevice = RTStrNCmp(pszFilename, "/dev/disk", sizeof("/dev/disk") - 1) == 0 ? true : false;
403 if (!fIsBlockDevice)
404 fIsBlockDevice = RTStrNCmp(pszFilename, "/dev/rdisk", sizeof("/dev/rdisk") - 1) == 0 ? true : false;
405 return fIsBlockDevice;
406}
407
408
409DECLHIDDEN(int) HBDMgrClaimBlockDevice(HBDMGR hHbdMgr, const char *pszFilename)
410{
411 PHBDMGRINT pThis = hHbdMgr;
412 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
413 AssertReturn(HBDMgrIsBlockDevice(pszFilename), VERR_INVALID_PARAMETER);
414
415 int rc = VINF_SUCCESS;
416 PHBDMGRDEV pDev = hbdMgrDevFindByName(pThis, pszFilename);
417 if (!pDev)
418 {
419 DADiskRef hDiskRef = DADiskCreateFromBSDName(kCFAllocatorDefault, pThis->hSessionRef, pszFilename);
420 if (hDiskRef)
421 {
422 HBDMGRDACLBKARGS CalllbackArgs;
423 CalllbackArgs.pThis = pThis;
424 CalllbackArgs.rcDA = kDAReturnSuccess;
425
426 /* Claim the device. */
427 DADiskClaim(hDiskRef, kDADiskClaimOptionDefault, NULL, NULL, hbdMgrDACallbackComplete, &CalllbackArgs);
428 rc = RTSemEventWait(pThis->hEvtCallback, 120 * RT_MS_1SEC);
429 if ( RT_SUCCESS(rc)
430 && CalllbackArgs.rcDA == kDAReturnSuccess)
431 {
432 /* Unmount anything which might be mounted. */
433 DADiskUnmount(hDiskRef, kDADiskUnmountOptionWhole, hbdMgrDACallbackComplete, &CalllbackArgs);
434 rc = RTSemEventWait(pThis->hEvtCallback, 120 * RT_MS_1SEC);
435 if ( RT_SUCCESS(rc)
436 && ( CalllbackArgs.rcDA == kDAReturnSuccess
437 || CalllbackArgs.rcDA == kDAReturnNotMounted))
438 {
439 pDev = (PHBDMGRDEV)RTMemAllocZ(sizeof(HBDMGRDEV));
440 if (RT_LIKELY(pDev))
441 {
442 pDev->hDiskRef = hDiskRef;
443 RTSemFastMutexRequest(pThis->hMtxList);
444 RTListAppend(&pThis->ListClaimed, &pDev->ListNode);
445 RTSemFastMutexRelease(pThis->hMtxList);
446 rc = VINF_SUCCESS;
447 }
448 else
449 rc = VERR_NO_MEMORY;
450 }
451 else if (RT_SUCCESS(rc))
452 rc = hbdMgrDAReturn2VBoxStatus(CalllbackArgs.rcDA);
453 }
454 else if (RT_SUCCESS(rc))
455 rc = hbdMgrDAReturn2VBoxStatus(CalllbackArgs.rcDA);
456
457 if (RT_FAILURE(rc))
458 CFRelease(hDiskRef);
459 }
460 else
461 rc = VERR_NO_MEMORY;
462 }
463 else
464 rc = VERR_ALREADY_EXISTS;
465
466 return rc;
467}
468
469
470DECLHIDDEN(int) HBDMgrUnclaimBlockDevice(HBDMGR hHbdMgr, const char *pszFilename)
471{
472 PHBDMGRINT pThis = hHbdMgr;
473 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
474
475 RTSemFastMutexRequest(pThis->hMtxList);
476 int rc = VINF_SUCCESS;
477 PHBDMGRDEV pDev = hbdMgrDevFindByName(pThis, pszFilename);
478 if (pDev)
479 hbdMgrDevUnclaim(pDev);
480 else
481 rc = VERR_NOT_FOUND;
482 RTSemFastMutexRelease(pThis->hMtxList);
483
484 return rc;
485}
486
487
488DECLHIDDEN(bool) HBDMgrIsBlockDeviceClaimed(HBDMGR hHbdMgr, const char *pszFilename)
489{
490 PHBDMGRINT pThis = hHbdMgr;
491 AssertPtrReturn(pThis, false);
492
493 RTSemFastMutexRequest(pThis->hMtxList);
494 PHBDMGRDEV pIt = hbdMgrDevFindByName(pThis, pszFilename);
495 RTSemFastMutexRelease(pThis->hMtxList);
496
497 return pIt ? true : false;
498}
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