forked from dustinrue/ControlPlane
/
NSWorkspace_RBAdditions.m
180 lines (167 loc) · 7.04 KB
/
NSWorkspace_RBAdditions.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
180
//
// NSWorkspace_RBAdditions.m
// PathProps
//
// Created by Rainer Brockerhoff on 10/04/2007.
// Copyright 2007 Rainer Brockerhoff. All rights reserved.
//
#import "Sparkle.h"
#import "NSWorkspace_RBAdditions.h"
#include <IOKit/IOKitLib.h>
#include <sys/mount.h>
#include <mach/mach.h>
NSString* NSWorkspace_RBfstypename = @"NSWorkspace_RBfstypename";
NSString* NSWorkspace_RBmntonname = @"NSWorkspace_RBmntonname";
NSString* NSWorkspace_RBmntfromname = @"NSWorkspace_RBmntfromname";
NSString* NSWorkspace_RBdeviceinfo = @"NSWorkspace_RBdeviceinfo";
NSString* NSWorkspace_RBimagefilepath = @"NSWorkspace_RBimagefilepath";
NSString* NSWorkspace_RBconnectiontype = @"NSWorkspace_RBconnectiontype";
NSString* NSWorkspace_RBpartitionscheme = @"NSWorkspace_RBpartitionscheme";
NSString* NSWorkspace_RBserverURL = @"NSWorkspace_RBserverURL";
// This static funtion concatenates two strings, but first checks several possibilities...
// like one or the other nil, or one containing the other already.
static NSString* AddPart(NSString* first,NSString* second) {
if (!second) {
return first;
}
second = [second stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (first) {
if ([first rangeOfString:second options:NSCaseInsensitiveSearch].location==NSNotFound) {
if ([second rangeOfString:first options:NSCaseInsensitiveSearch].location==NSNotFound) {
return [NSString stringWithFormat:@"%@; %@",first,second];
}
return second;
}
return first;
}
return second;
}
// This static functions recurses "upwards" over the IO registry. Returns strings that are concatenated
// and ultimately end up under the NSWorkspace_RBdeviceinfo key.
// This isn't too robust in that it assumes that objects returned by the objectForKey methods are
// either strings or dictionaries. A "standard" implementations would use either only CoreFoundation and
// IOKit calls for this, or do more robust type checking on the returned objects.
//
// Also notice that this works as determined experimentally in 10.4.9, there's no official docs I could find.
// YMMV, and it may stop working in any new version of Mac OS X.
static NSString* CheckParents(io_object_t thing,NSString* part,NSMutableDictionary* dict) {
NSString* result = part;
io_iterator_t parentsIterator = 0;
kern_return_t kernResult = IORegistryEntryGetParentIterator(thing,kIOServicePlane,&parentsIterator);
if ((kernResult==KERN_SUCCESS)&&parentsIterator) {
io_object_t nextParent = 0;
while ((nextParent = IOIteratorNext(parentsIterator))) {
NSDictionary* props = nil;
NSString* image = nil;
NSString* partition = nil;
NSString* connection = nil;
kernResult = IORegistryEntryCreateCFProperties(nextParent,(CFMutableDictionaryRef*)&props,kCFAllocatorDefault,0);
if (IOObjectConformsTo(nextParent,"IOApplePartitionScheme")) {
partition = [props objectForKey:@"Content Mask"];
} else if (IOObjectConformsTo(nextParent,"IOMedia")) {
partition = [props objectForKey:@"Content"];
} else if (IOObjectConformsTo(nextParent,"IODiskImageBlockStorageDeviceOutKernel")) {
NSData* data = nil;
if ((data = [[props objectForKey:@"Protocol Characteristics"] objectForKey:@"Virtual Interface Location Path"])) {
image = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
}
} else if (IOObjectConformsTo(nextParent,"IOHDIXHDDriveInKernel")) {
image = [props objectForKey:@"KDIURLPath"];
}
NSDictionary* subdict;
if ((subdict = [props objectForKey:@"Protocol Characteristics"])) {
connection = [subdict objectForKey:@"Physical Interconnect"];
} else {
connection = [props objectForKey:@"Physical Interconnect"];
}
if (connection) {
[dict setObject:AddPart([dict objectForKey:NSWorkspace_RBconnectiontype],connection) forKey:NSWorkspace_RBconnectiontype];
}
if (partition) {
[dict setObject:partition forKey:NSWorkspace_RBpartitionscheme];
}
if (image) {
[dict setObject:image forKey:NSWorkspace_RBimagefilepath];
}
NSString* value;
if ((subdict = [props objectForKey:@"Device Characteristics"])) {
if ((value = [subdict objectForKey:@"Product Name"])) {
result = AddPart(result,value);
}
if ((value = [subdict objectForKey:@"Product Revision Level"])) {
result = AddPart(result,value);
}
if ((value = [subdict objectForKey:@"Vendor Name"])) {
result = AddPart(result,value);
}
}
if ((value = [props objectForKey:@"USB Serial Number"])) {
result = AddPart(result,value);
}
if ((value = [props objectForKey:@"USB Vendor Name"])) {
result = AddPart(result,value);
}
io_name_t ts; // char[128]
NSString* cls = ( IOObjectGetClass (nextParent, ts) == KERN_SUCCESS ) ? [NSString stringWithUTF8String:ts ] : nil;
if (![cls isEqualToString:@"IOPCIDevice"]) {
// Uncomment the following line to have the device tree dumped to the console.
// NSLog(@"=================================> %@:%@\n",cls,props);
result = CheckParents(nextParent,result,dict);
}
if (props) { CFRelease(props); }
IOObjectRelease(nextParent);
}
}
if (parentsIterator) {
IOObjectRelease(parentsIterator);
}
return result;
}
@implementation NSWorkspace (NSWorkspace_RBAdditions)
// Returns a NSDictionary with properties for the path. See details in the .h file.
// This assumes that the length of path is less than PATH_MAX (currently 1024 characters).
- (NSDictionary*)propertiesForPath:(NSString*)path {
const char* ccpath = [path fileSystemRepresentation];
NSMutableDictionary* result = nil;
struct statfs fs = {};
if (!statfs(ccpath,&fs)) {
NSString* from = [NSString stringWithUTF8String:fs.f_mntfromname];
result = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithUTF8String:fs.f_fstypename],NSWorkspace_RBfstypename,
[NSString stringWithUTF8String:fs.f_mntonname],NSWorkspace_RBmntonname,
nil];
const char* devstring = "/dev/";
size_t devstringlen = 5;
if (strncmp(fs.f_mntfromname,devstring,devstringlen)==0) {
// For a local volume,get the IO registry tree and search it for further info.
mach_port_t masterPort = 0;
io_iterator_t mediaIterator = 0;
kern_return_t kernResult = IOMasterPort(bootstrap_port,&masterPort);
if (kernResult==KERN_SUCCESS) {
CFMutableDictionaryRef classesToMatch = IOBSDNameMatching(masterPort,0,&fs.f_mntfromname[devstringlen]);
if (classesToMatch) {
kernResult = IOServiceGetMatchingServices(masterPort,classesToMatch,&mediaIterator);
if ((kernResult==KERN_SUCCESS)&&mediaIterator) {
io_object_t firstMedia = 0;
while ((firstMedia = IOIteratorNext(mediaIterator))) {
NSString* stuff = CheckParents(firstMedia,nil,result);
if (stuff) {
[result setObject:stuff forKey:NSWorkspace_RBdeviceinfo];
}
IOObjectRelease(firstMedia);
}
}
}
}
if (mediaIterator) {
IOObjectRelease(mediaIterator);
}
if (masterPort) {
mach_port_deallocate(mach_task_self(),masterPort);
}
}
[result setObject:from forKey:NSWorkspace_RBmntfromname];
}
return result;
}
@end