2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <dispatch/dispatch.h>
27 #include <DiskArbitration/DiskArbitration.h>
29 #include <security_utilities/logging.h>
30 #include <security_utilities/cfutilities.h>
31 #include <security_utilities/unix++.h>
33 #include "SecTranslocateDANotification.hpp"
34 #include "SecTranslocateShared.hpp"
35 #include "SecTranslocateUtilities.hpp"
37 #define DA_FRAMEWORK_PATH "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration"
40 namespace SecTranslocate
{
42 typedef CFDictionaryRef (*DiskCopyDescription_t
)(DADiskRef
);
43 typedef DASessionRef (*SessionCreate_t
) (CFAllocatorRef
);
44 typedef void (*SessionSetDispatchQueue_t
)(DASessionRef
,dispatch_queue_t
);
45 typedef void (*RegisterDiskDisappearedCallback_t
) (DASessionRef
, CFDictionaryRef
, DADiskDisappearedCallback
, void*);
46 typedef void (*RegisterDiskUnmountApprovalCallback_t
) (DASessionRef
, CFDictionaryRef
, DADiskUnmountApprovalCallback
, void*);
47 typedef void (*UnregisterCallback_t
)(DASessionRef
, void*, void*);
49 class DiskArbitrationProxy
52 static DiskArbitrationProxy
* get();
54 inline CFDictionaryRef
diskCopyDescription(DADiskRef disk
) const
55 { return pDiskCopyDescription
? pDiskCopyDescription(disk
) : NULL
; };
56 inline DASessionRef
sessionCreate (CFAllocatorRef allocator
) const
57 { return pSessionCreate
? pSessionCreate(allocator
) : NULL
; };
58 inline void sessionSetDispatchQueue (DASessionRef s
, dispatch_queue_t q
) const
59 { if(pSessionSetDispatchQueue
) pSessionSetDispatchQueue(s
,q
); };
60 inline void registerDiskDisappearedCallback (DASessionRef s
, CFDictionaryRef d
, DADiskDisappearedCallback c
, void* x
) const
61 { if(pRegisterDiskDisappearedCallback
) pRegisterDiskDisappearedCallback(s
,d
,c
,x
); };
62 inline void registerDiskUnmountApprovalCallback (DASessionRef s
, CFDictionaryRef d
, DADiskUnmountApprovalCallback c
, void* x
) const
63 { if(pRegisterDiskUnmountApprovalCallback
) pRegisterDiskUnmountApprovalCallback(s
,d
,c
,x
); };
64 inline void unregisterCallback (DASessionRef s
, void* c
, void* x
) const
65 { if(pUnregisterCallback
) pUnregisterCallback(s
,c
,x
); };
66 inline CFDictionaryRef
diskDescriptionMatchVolumeMountable() const
67 { return pDiskDescriptionMatchVolumeMountable
? *pDiskDescriptionMatchVolumeMountable
: NULL
; };
68 inline CFStringRef
diskDescriptionVolumePathKey() const
69 { return pDiskDescriptionVolumePathKey
? *pDiskDescriptionVolumePathKey
: NULL
; };
72 DiskArbitrationProxy();
75 DiskCopyDescription_t pDiskCopyDescription
;
76 SessionCreate_t pSessionCreate
;
77 SessionSetDispatchQueue_t pSessionSetDispatchQueue
;
78 RegisterDiskDisappearedCallback_t pRegisterDiskDisappearedCallback
;
79 RegisterDiskUnmountApprovalCallback_t pRegisterDiskUnmountApprovalCallback
;
80 UnregisterCallback_t pUnregisterCallback
;
81 CFDictionaryRef
* pDiskDescriptionMatchVolumeMountable
;
82 CFStringRef
* pDiskDescriptionVolumePathKey
;
85 DiskArbitrationProxy::DiskArbitrationProxy()
87 handle
= checkedDlopen(DA_FRAMEWORK_PATH
, RTLD_LAZY
| RTLD_NOLOAD
);
89 pDiskCopyDescription
= (DiskCopyDescription_t
) checkedDlsym(handle
, "DADiskCopyDescription");
90 pSessionCreate
= (SessionCreate_t
) checkedDlsym(handle
, "DASessionCreate");
92 pSessionSetDispatchQueue
= (SessionSetDispatchQueue_t
) checkedDlsym(handle
, "DASessionSetDispatchQueue");
93 pRegisterDiskDisappearedCallback
= (RegisterDiskDisappearedCallback_t
) checkedDlsym(handle
, "DARegisterDiskDisappearedCallback");
94 pRegisterDiskUnmountApprovalCallback
= (RegisterDiskUnmountApprovalCallback_t
) checkedDlsym(handle
, "DARegisterDiskUnmountApprovalCallback");
95 pUnregisterCallback
= (UnregisterCallback_t
) checkedDlsym(handle
, "DAUnregisterCallback");
96 pDiskDescriptionMatchVolumeMountable
= (CFDictionaryRef
*) checkedDlsym(handle
, "kDADiskDescriptionMatchVolumeMountable");
97 pDiskDescriptionVolumePathKey
= (CFStringRef
*) checkedDlsym(handle
, "kDADiskDescriptionVolumePathKey");
100 DiskArbitrationProxy
* DiskArbitrationProxy::get()
102 static dispatch_once_t initialized
;
103 static DiskArbitrationProxy
* me
= NULL
;
104 __block exception_ptr
exception(0);
106 dispatch_once(&initialized
, ^{
109 me
= new DiskArbitrationProxy();
113 Syslog::critical("SecTranslocate: error while creating DiskArbitrationProxy");
114 exception
= current_exception();
122 rethrow_exception(exception
); //already logged in this case
126 Syslog::critical("SecTranslocate: DiskArbitrationProxy initialization has failed");
127 UnixError::throwMe(EINVAL
);
134 For Disk Arbitration need to
135 1. create a session and hold on to it.
136 2. associate it with a queue
137 3. register for call backs (DADiskDisappearedCallback and DADiskUnmountApprovalCallback)
138 4. provide a function to get the mounton path from DADiskref
139 5. Return a dissenter if unmount is gonna fail because something is in use (i.e. if my unmount fails)
142 /* Returns false if we failed an unmount call. anything else returns true */
143 static bool cleanupDisksOnVolume(DADiskRef disk
)
149 DiskArbitrationProxy
*dap
= DiskArbitrationProxy::get();
150 CFRef
<CFDictionaryRef
> dict
= dap
->diskCopyDescription(disk
);
154 Syslog::error("SecTranslocate:disk cleanup, failed to get disk description");
155 UnixError::throwMe(EINVAL
);
158 CFURLRef fspath
= (CFURLRef
)CFDictionaryGetValue(dict
, dap
->diskDescriptionVolumePathKey());
162 //For the disk disappeared call back, it looks like we won't get a volume path so we'll keep the empty string
163 fspathString
= cfString(fspath
);
166 result
= destroyTranslocatedPathsForUserOnVolume(fspathString
);
170 // This function is called from inside a DiskArbitration callback so we need to consume the exception
171 // more specific errors are assumed to be logged by the thrower
172 Syslog::error("SecTranslocate: DiskArbitration callback: failed to clean up mountpoint(s) related to volume: %s",
173 fspathString
.empty() ? "unknown" : fspathString
.c_str());
179 static void diskDisappearedCallback(DADiskRef disk
, void* context
)
181 (void)cleanupDisksOnVolume(disk
);
184 static DADissenterRef
unmountApprovalCallback(DADiskRef disk
, void *context
)
186 (void)cleanupDisksOnVolume(disk
);
187 return NULL
; //For now, we won't raise a dissent, just let the unmount fail. The dissent text would get used by UI.
190 DANotificationMonitor::DANotificationMonitor(dispatch_queue_t q
)
192 DiskArbitrationProxy
*dap
= DiskArbitrationProxy::get();
195 Syslog::critical("SecTranslocate::DANotificationMonitor initialized without a queue.");
196 UnixError::throwMe(EINVAL
);
199 diskArbitrationSession
= dap
->sessionCreate(kCFAllocatorDefault
);
200 if(!diskArbitrationSession
)
202 Syslog::critical("SecTranslocate: Failed to create the disk arbitration session");
203 UnixError::throwMe(ENOMEM
);
206 dap
->sessionSetDispatchQueue(diskArbitrationSession
, q
);
207 /* register so we can cleanup from force unmounts */
208 dap
->registerDiskDisappearedCallback( diskArbitrationSession
, dap
->diskDescriptionMatchVolumeMountable(), diskDisappearedCallback
, NULL
);
209 /* register so we can clean up pre-unmount */
210 dap
->registerDiskUnmountApprovalCallback( diskArbitrationSession
, dap
->diskDescriptionMatchVolumeMountable(), unmountApprovalCallback
, NULL
);
213 DANotificationMonitor::~DANotificationMonitor()
215 DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession
,(void*)diskDisappearedCallback
, NULL
);
216 DiskArbitrationProxy::get()->unregisterCallback(diskArbitrationSession
,(void*)unmountApprovalCallback
, NULL
);
217 CFRelease(diskArbitrationSession
);
220 } //namespace SecTranslocate
221 } //namespace Security