Disabling OS-X Device Removal Warnings In Yosemite
If you're anything like me you've probably seen the Disk Not Ejected Properly message so many times that you've forgotten how annoying it is.. despite the fact that you never even wrote anything to the usb drive that you're yanking out of your Mac the operating system insists that you remove it "correctly". You're supposed to click the eject button. Why? I dunno, Apple seems to think that if you can write to it then the operating system might have written to it - and being Apple, they probably have. Well ya know what? Fuck you, I won't do what you tell me.
Note: this will not fix your external drive disconnecting when your Mac goes to sleep. For that problem try disabling "put hard drive to sleep when possible" in both the Battery and Power Adapter tabs of the OS energy saver settings. What appears below is a dirty hack for deliberately removing a warning that Apple insists on showing you every single time you yank out a usb stick.
The short version. WARNING, this may break your Mac! Be careful! Download my hacked DiskArbitrationAgent. Open a terminal and enter the following commands:
cd /System/Library/Frameworks/DiskArbitration.framework/Versions/A/Support sudo cp DiskArbitrationAgent DiskArbitrationAgent.original password: [Enter your password] sudo cp ~/Downloads/DiskArbitrationAgent . killall DiskArbitrationAgent
The long version. What does this do? Read on.
In my quest to remove this annoying and pointless warning, I started with a Google search and turned up various answers of the form "don't do that" and guides on how to press the eject button. Gee, thanks! One more helpful answer suggested disabling the User Notification Center - which is akin to cutting off your nose to spite your face. Okay, so I get that Apple doesn't want me to turn off this warning, but I don't care what they think. I'm turning it off.
My next course of action was bargaining. I figured the engineers at Apple are pretty reasonable, they wouldn't throw this annoying warning in my face if I mounted the drive read-only, surely? Using sudo vifs I added a line to my /etc/fstab with an appropriate UUID. After inserting the usb drive I ran mount to ensure it was mounted read-only, it was. I then proceeded to yank out the drive without pressing Eject and still got the cursed warning! Apple engineers are not reasonable.
In fact, I actually tracked down the code that does the appropriate check. It's in DARequest.c in diskarbitrationd and it looks like this:
if ( DADiskGetState( disk, kDADiskStateZombie ) ) { DARequestSetState( request, kDARequestStateStagedApprove, TRUE ); if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue ) { DADialogShowDeviceRemoval( disk ); } }
Hey idiots, MediaWritable means the media is writable, not that the drive is mounted read-write. You're displaying a warning for a potential problem that is impossible. Way to go!
So where does this warning come from? It's displayed as a result of that DADialogShowDeviceRemoval() call, but that's just a message sending wrapper. I found out. The offending code is in DADialog.m in DiskArbitrationAgent and it looks like this:
static NSString * __kDADialogLocalizedStringDeviceRemovalKey = @"Eject \"%@\" before disconnecting or turning it off."; static NSString * __kDADialogLocalizedStringDeviceRemovalTitleKey = @"Disk Not Ejected Properly"; ... void DADialogShowDeviceRemoval( DADiskRef disk ) { NSUserNotificationCenter * center; center = [ NSUserNotificationCenter _centerForIdentifier: @_kDAAgentName type: _NSUserNotificationCenterTypeSystem ]; if ( center ) { NSUserNotification * notification; notification = [ [ NSUserNotification alloc ] init ]; if ( notification ) { NSBundle * bundle; bundle = [ NSBundle bundleWithPath: __kDADialogLocalizedStringBundlePath ]; if ( bundle ) { NSDictionary * description; description = ( __bridge_transfer id ) DADiskCopyDescription( disk ); if ( description ) { NSString * name; name = [ description objectForKey: ( __bridge id ) kDADiskDescriptionVolumeNameKey ]; if ( name == NULL ) { name = __DALocalizedStringInBundle( @"Untitled", bundle ); } notification.hasActionButton = FALSE; notification.informativeText = [ NSString stringWithFormat: __DALocalizedStringInBundle( __kDADialogLocalizedStringDeviceRemovalKey, bundle ), name ]; notification.title = __DALocalizedStringInBundle( __kDADialogLocalizedStringDeviceRemovalTitleKey, bundle ); notification._imageURL = [ NSURL fileURLWithPath: @"/System/Library/CoreServices/CoreTypes.bundle /Contents/Resources/FinderIcon.icns" ]; notification._persistent = FALSE; [ center deliverNotification: notification ]; } } } } }
You can find the DiskArbitrationAgent in /System/Library/Frameworks/DiskArbitration.framework/Versions/A/Support and on my Mac it's 22992 bytes long. Using otool -tV we see the offending code:
0000000100000faf pushq %rbp 0000000100000fb0 movq %rsp, %rbp 0000000100000fb3 pushq %r15 0000000100000fb5 pushq %r14 0000000100000fb7 pushq %r13 0000000100000fb9 pushq %r12 0000000100000fbb pushq %rbx 0000000100000fbc subq $0x68, %rsp 0000000100000fc0 movq %rdi, %r14 0000000100000fc3 movq 0x1556(%rip), %rdi ## Objc class ref: _OBJC_CLASS_$_NSUserNotificationCenter 0000000100000fca movq 0x14b7(%rip), %rsi ## Objc selector ref: _centerForIdentifier:type: 0000000100000fd1 leaq 0x13a8(%rip), %rdx ## Objc cfstring ref: @"com.apple.DiskArbitration.DiskArbitrationAgent" 0000000100000fd8 movl $0x2, %ecx 0000000100000fdd callq *0x10b5(%rip) ## Objc message: +[NSUserNotificationCenter _centerForIdentifier:type:] 0000000100000fe3 movq %rax, %rdi 0000000100000fe6 callq 0x1000019e6 ## symbol stub for: _objc_retainAutoreleasedReturnValue 0000000100000feb movq %rax, %rbx 0000000100000fee testq %rbx, %rbx ... 00000001000012de leaq 0xf7b(%rip), %rdx ## Objc cfstring ref: @"Eject "%@" before disconnecting or turning it off." ...
If we hexdump -C DiskArbitrationAgent we can see the actual bytes too:
00000fa0 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d c3 55 |H...[A\A]A^A_].U| 00000fb0 48 89 e5 41 57 41 56 41 55 41 54 53 48 83 ec 68 |H..AWAVAUATSH..h|
Yes, the text segment of Mach-O images nicely correspond in memory as they do on disk - at least in non-fat binaries. Okay, that's boring to just about everyone except me. How do we get rid of this warning? That's easy, all we have to do is insert a c3 at faf. You can use a hex editor, but I just wrote this tiny program:
#include <stdio.h> int main() { FILE *f = fopen("DiskArbitrationAgent", "r+"); fseek(f, 0xfaf, SEEK_SET); unsigned char r = 0xc3; fwrite(&r, 1, 1, f); fclose(f); return 0; }
I recommend making a backup of the original file before fiddling with it. We then need to killall DiskArbitrationAgent and we're done. Too hard? Here's my hacked DiskArbitrationAgent.
I hoped you have enjoyed this exercise and don't trash your Mac. :)
About time you woke this blog up.
ReplyDeleteC, asm, and hex... oh my! That isn't how they do it in StarTrek!
The thing that kills me is I absolutely know what I what in a coding environment but will never see it.
I've patched the El Capitan version (not much changes has been done since offset is +1B) - though, code signing prevents it from running obviously.
ReplyDeleteCODE SIGNING: process 76511[DiskArbitrationA]: rejecting invalid page at address 0x10d52d000 from offset 0x0 in file "/System/Library/Frameworks/DiskArbitration.framework/Versions/A/Support/DiskArbitrationAgent" (cs_mtime:1435943981.0 != mtime:1442310526.0) (signed:1 validated:1 tainted:1 wpmapped:0 slid:0)
Heh, obviously. Fix the bug? Nope. Make it harder for us to fix it ourselves? Of course!
DeleteIt does seem that System Integrity Protection can be disabled, but it's all or nothing.