forked from kainjow/Semulov
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SLDiskImageManager.m
179 lines (151 loc) · 4.88 KB
/
SLDiskImageManager.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//
// SLDiskImageManager.h
// Semulov
//
// Created by Kevin Wojniak on 7/20/14.
// Copyright (c) 2014 Kevin Wojniak. All rights reserved.
//
#import "SLDiskImageManager.h"
#import "NSTaskAdditions.h"
#import <pthread.h>
@interface SLReadWriteLock : NSObject
- (void)readLock;
- (void)writeLock;
- (void)unlock;
@end
@implementation SLReadWriteLock
{
pthread_rwlock_t _lock;
}
- (instancetype)init
{
if ((self = [super init]) != nil) {
if (pthread_rwlock_init(&_lock, NULL) != 0) {
NSLog(@"rwlock_init error: %s", strerror(errno));
}
}
return self;
}
- (void)dealloc
{
if (pthread_rwlock_destroy(&_lock) != 0) {
NSLog(@"rwlock_destroy error: %s", strerror(errno));
}
}
- (void)readLock
{
if (pthread_rwlock_rdlock(&_lock) != 0) {
NSLog(@"rwlock_rdlock error: %s", strerror(errno));
}
}
- (void)writeLock
{
if (pthread_rwlock_wrlock(&_lock) != 0) {
NSLog(@"rwlock_wrlock error: %s", strerror(errno));
}
}
- (void)unlock
{
if (pthread_rwlock_unlock(&_lock) != 0) {
NSLog(@"rwlock_unlock error: %s", strerror(errno));
}
}
@end
@implementation SLDiskImageManager
{
NSDictionary *_info;
SLReadWriteLock *_infoLock;
}
+ (NSDictionary *)infoPlist
{
NSString *plistStr = [NSTask outputStringForTaskAtPath:@"/usr/bin/hdiutil" arguments:@[@"info", @"-plist"] encoding:NSUTF8StringEncoding];
// sometimes hdiutil returns an error in the first line or so of it's output.
// so we try to determine if an error exists, and skip past it to the xml
NSString *xmlStr = @"<?xml";
NSRange xmlRange = [plistStr rangeOfString:xmlStr];
if (xmlRange.location == NSNotFound) {
// not valid xml?!
return nil;
}
if (xmlRange.location > 0) {
// scan up to XML
NSScanner *scanner = [NSScanner scannerWithString:plistStr];
[scanner scanUpToString:xmlStr intoString:nil];
plistStr = [plistStr substringFromIndex:[scanner scanLocation]];
}
NSDictionary *plistDict = [plistStr propertyList];
if (!plistDict || ![plistDict isKindOfClass:[NSDictionary class]]) {
return nil;
}
return plistDict;
}
- (instancetype)init
{
if ((self = [super init]) != nil) {
_infoLock = [[SLReadWriteLock alloc] init];
}
return self;
}
- (void)reloadInfo:(dispatch_block_t)finishedHandler
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
NSDictionary *info = [[self class] infoPlist];
[self->_infoLock writeLock];
self->_info = info;
[self->_infoLock unlock];
if (finishedHandler) {
dispatch_async(dispatch_get_main_queue(), finishedHandler);
}
}
});
}
- (NSString *)diskImageForVolume:(NSString *)volume
{
NSString *ret = nil;
[_infoLock readLock];
for (NSDictionary *imagesDict in [_info objectForKey:@"images"]) {
NSString *imagePath = imagesDict[@"image-path"];
// if .dmg is mounted from safari, imagePath will be the .dmg within the .download file
NSRange dotDownloadRange = [imagePath rangeOfString:@".download"];
if (dotDownloadRange.location != NSNotFound) {
imagePath = [imagePath substringToIndex:dotDownloadRange.location];
}
for (NSDictionary *sysEntity in imagesDict[@"system-entities"]) {
NSString *mountPoint = [sysEntity objectForKey:@"mount-point"];
if ([mountPoint isEqualToString:volume]) {
// make a copy so we're not using an object inside _info which may be
// deallocated after the lock is released.
ret = [imagePath copy];
break;
}
}
}
[_infoLock unlock];
return ret;
}
- (NSString *)diskImageForDiskID:(NSString *)diskID
{
NSString *ret = nil;
[_infoLock readLock];
for (NSDictionary *imagesDict in [_info objectForKey:@"images"]) {
NSString *imagePath = imagesDict[@"image-path"];
// if .dmg is mounted from safari, imagePath will be the .dmg within the .download file
NSRange dotDownloadRange = [imagePath rangeOfString:@".download"];
if (dotDownloadRange.location != NSNotFound) {
imagePath = [imagePath substringToIndex:dotDownloadRange.location];
}
for (NSDictionary *sysEntity in imagesDict[@"system-entities"]) {
NSString *devEntry = [[sysEntity objectForKey:@"dev-entry"] lastPathComponent];
if ([devEntry isEqualToString:diskID]) {
// make a copy so we're not using an object inside _info which may be
// deallocated after the lock is released.
ret = [imagePath copy];
break;
}
}
}
[_infoLock unlock];
return ret;
}
@end