1 | //␊ |
2 | // PartitionInfoElement.mm␊ |
3 | // ChameleonPrefPane␊ |
4 | //␊ |
5 | // Powerful Partition Info class utility␊ |
6 | //␊ |
7 | // Created by Rekursor on 11-11-12.␊ |
8 | //␊ |
9 | ␊ |
10 | #import "PartitionInfoElement.h"␊ |
11 | #import "ShellProcess.h"␊ |
12 | #include "string_util.h"␊ |
13 | ␊ |
14 | static NSUInteger sHdRedirTable[MAX_HD];␊ |
15 | ␊ |
16 | @implementation PartitionInfoElement␊ |
17 | ␊ |
18 | @synthesize descDict, bsdName, vUUID, vKind, vName, vPath, mediaPath, mediaRemovable, devInternal, devProtocol;␊ |
19 | @synthesize vAliasName, hidden, bootInfo;␊ |
20 | ␊ |
21 | /// Create a list of all bsd partitions␊ |
22 | +(NSArray*) createBSDPartitionList␊ |
23 | {␊ |
24 | ␉NSMutableArray* arr = [[[NSMutableArray alloc] init] autorelease];␊ |
25 | ␉␊ |
26 | ␉char line[256];␊ |
27 | ␉ShellProcess p;␊ |
28 | ␉␊ |
29 | ␉p.open("ls -1 /dev/disk*");␊ |
30 | ␉while( p.get_line(line, sizeof(line)-1))␊ |
31 | ␉{␊ |
32 | ␉␉size_t l = strlen(line);␊ |
33 | ␉␉if (l==0 || strstr(line, "ls:")!=NULL) continue;␊ |
34 | ␉␉if (line[l-1]) line[l-1]='\0';␊ |
35 | ␉␉const char * p = strstr(line, "disk");␊ |
36 | ␉␉NSString* s = [NSString stringWithUTF8String: p] ;␊ |
37 | ␉␉[arr addObject: s];␉␊ |
38 | ␉␉␊ |
39 | ␉}␊ |
40 | ␉p.close();␊ |
41 | ␉␊ |
42 | ␉return arr;␊ |
43 | }␊ |
44 | ␊ |
45 | /// Get the boot information string, for chameleon only for now ...␊ |
46 | -(NSString*) bootDescription␊ |
47 | {␊ |
48 | NSString* extractCmd = @"test -f \"%@boot\" && strings \"%@boot\" | sed -nE 's!^Darwin/x86 boot.+(Chameleon.+)!\\1!p'";␊ |
49 | NSString* extractCmd2 = @"test -f \"%@mach_kernel\" && strings \"%@mach_kernel\" | sed -nE 's!^(Darwin Kernel Version.+)(\\: .*$)!\\1!p'";␊ |
50 | NSString* localPath = [[self.vPath ␊ |
51 | stringByReplacingOccurrencesOfString: @"file://localhost"␊ |
52 | withString:@""] ␊ |
53 | stringByReplacingOccurrencesOfString:@"%20" withString:@" "];␊ |
54 | ␊ |
55 | NSString* shellCmd = [NSString stringWithFormat:extractCmd, localPath, localPath ];␊ |
56 | NSString* shellCmd2 = [NSString stringWithFormat:extractCmd2, localPath, localPath ];␊ |
57 | std::string line = trim(ShellProcess([shellCmd UTF8String]).get_line(),"\n");␊ |
58 | ␊ |
59 | if (line.length()>0)␊ |
60 | {␊ |
61 | return [NSString stringWithUTF8String:line.c_str()];␊ |
62 | }␊ |
63 | else if ( (line=trim(ShellProcess([shellCmd2 UTF8String]).get_line(),"\n")).length()>0)␊ |
64 | {␊ |
65 | return [NSString stringWithUTF8String:line.c_str()];␊ |
66 | }␊ |
67 | else␊ |
68 | return @"Unknown booter version";␊ |
69 | }␊ |
70 | ␊ |
71 | ␊ |
72 | /// redirection table for disk swapping␊ |
73 | +(NSUInteger*) hdRedirTable␊ |
74 | {␊ |
75 | ␉return sHdRedirTable;␊ |
76 | }␊ |
77 | ␊ |
78 | /// redirection table for disk swapping␊ |
79 | /// extract disk number from bsdname spec␊ |
80 | -(int) diskNumber␊ |
81 | {␊ |
82 | ␉int newDiskNum = (diskNum>=0 && diskNum <MAX_HD) ? sHdRedirTable[diskNum] : diskNum;␊ |
83 | ␉return newDiskNum;␊ |
84 | }␊ |
85 | ␊ |
86 | /// extract partition number from bsdname spec␊ |
87 | -(int) partitionNumber␊ |
88 | {␊ |
89 | ␉return partNum; // todo extract d num from bsdname␊ |
90 | }␊ |
91 | ␊ |
92 | -(NSString*) hdString␊ |
93 | {␊ |
94 | ␉return [NSString stringWithFormat:@"hd(%d,%d)", [self diskNumber], [self partitionNumber]];␊ |
95 | }␊ |
96 | ␊ |
97 | -(void) setDiskNumber: (int) d␊ |
98 | {␊ |
99 | ␉diskNum = d;␊ |
100 | }␊ |
101 | ␊ |
102 | -(void) setPartitionNumber:(int) p␊ |
103 | {␊ |
104 | ␉partNum = p;␊ |
105 | }␊ |
106 | /// return true if partition extraction successfully executed, false otherwise␊ |
107 | -(bool) isValid␊ |
108 | {␊ |
109 | ␉␊ |
110 | ␉return (␊ |
111 | ␉␉␉err==0␊ |
112 | ␉␉␉&& [self isBootable] ␊ |
113 | ␉␉␉// with chameleon as much as possible␊ |
114 | ␉␉␉&& ![[self devProtocol] isEqual:@"Virtual Interface"]␊ |
115 | ␉␉␉);␊ |
116 | }␊ |
117 | ␊ |
118 | /// Extract a particular information from the disk partition dictionary from a key, generate a string value output␊ |
119 | -(NSString*) stringValueWithKey: (NSString*) key␊ |
120 | {␊ |
121 | ␉if (err) return @"";␊ |
122 | ␉CFTypeRef o = [descDict objectForKey: key];␊ |
123 | ␉if (o==nil) return @"";␊ |
124 | ␉CFStringRef sref = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@"), o);␊ |
125 | ␉NSString* s = [[((NSString*) sref) copy] autorelease];␊ |
126 | ␉CFRelease(sref);␊ |
127 | ␉return s;␊ |
128 | }␊ |
129 | ␊ |
130 | /// main extraction method create a dictionary containing all needed information from disk arbitration API␊ |
131 | - (int) extractInfoWithBSDName: (NSString*) name withinSession:(DASessionRef) session␊ |
132 | {␊ |
133 | ␉err = 0;␊ |
134 | ␉␊ |
135 | ␉NSRange rDisk = [name rangeOfString:@"disk"];␊ |
136 | ␉NSRange rS = [name rangeOfString:@"s" options:NSBackwardsSearch] ;␊ |
137 | ␉␊ |
138 | ␉if (name == nil ␊ |
139 | ␉␉|| [name length] < 7 ␊ |
140 | ␉␉|| rDisk.location == NSNotFound ␊ |
141 | ␉␉|| rS.location <5) ␊ |
142 | ␉{␊ |
143 | ␉␉err = EINVAL;␊ |
144 | ␉␉return err;␊ |
145 | ␉}␊ |
146 | ␉␉␉␉␉␉␉␉␉␉␉ ␊ |
147 | ␉// extract disk and partition number␊ |
148 | ␉int pos = rDisk.location+rDisk.length;␊ |
149 | ␉int pos2 = rS.location+rS.length;␊ |
150 | ␉NSString* sDisk = [name substringWithRange:␊ |
151 | ␉␉␉␉␉ NSMakeRange(pos, rS.location - pos) ];␊ |
152 | ␉NSString* sPart = [name substringFromIndex: pos2 ];␊ |
153 | ␉␊ |
154 | ␉bool mustRelease = (session == nil) ? true : false;␊ |
155 | ␉␊ |
156 | ␉if (session == nil)␊ |
157 | ␉{␊ |
158 | ␉␉session = DASessionCreate(NULL);␊ |
159 | ␉}␊ |
160 | ␉␊ |
161 | ␉if (session == NULL)␉err = EINVAL;␊ |
162 | ␉␊ |
163 | ␉if (err == 0) {␊ |
164 | ␉␉DADiskRef disk = DADiskCreateFromBSDName(NULL, session, [name UTF8String] );␊ |
165 | ␉␉if (disk == NULL) err = EINVAL;␊ |
166 | else␊ |
167 | {␊ |
168 | [self cleanup];␊ |
169 | CFDictionaryRef␉dr = DADiskCopyDescription(disk);␊ |
170 | if (dr!=NULL)␊ |
171 | {␊ |
172 | descDict = (NSDictionary*) dr;␊ |
173 | CFRelease(dr);␊ |
174 | }␊ |
175 | }␊ |
176 | if (disk) CFRelease(disk);␊ |
177 | ␉}␊ |
178 | ␊ |
179 | ␉if (err == 0) {␊ |
180 | ␉␉diskNum = [sDisk␉intValue];␊ |
181 | ␉␉partNum = [sPart␉intValue];␊ |
182 | ␉␉␊ |
183 | ␉␉self.vUUID = [self stringValueWithKey: @"DAVolumeUUID"];␊ |
184 | ␉␉self.vName = [self stringValueWithKey: @"DAVolumeName"];␊ |
185 | ␉␉self.vPath = [self stringValueWithKey: @"DAVolumePath"];␊ |
186 | ␉␉if (self.vName == nil || [vName length]==0)␊ |
187 | ␉␉␉self.vName = [[self.vPath lastPathComponent ] stringByReplacingOccurrencesOfString:@"%20" withString:@" "];␊ |
188 | ␉␉self.vAliasName = self.vName; // by default renamed = original part name ␊ |
189 | ␉␉self.vKind = [self stringValueWithKey: @"DAVolumeKind"];␊ |
190 | ␉␉self.mediaPath = [self stringValueWithKey: @"DAMediaPath"];␊ |
191 | ␉␉self.devProtocol = [self stringValueWithKey: @"DADeviceProtocol"];␊ |
192 | ␉␉self.bsdName = [NSString stringWithString:name];␊ |
193 | ␉␉␊ |
194 | ␉␉self.devInternal = (bool) [[descDict objectForKey: @"DADeviceInternal"] boolValue];␊ |
195 | ␉␉self.mediaRemovable = (bool) [[descDict objectForKey: @"DAMediaRemovable"] boolValue];␊ |
196 | ␉␉␊ |
197 | ␉}␊ |
198 | ␉␊ |
199 | ␉// Clean up.␊ |
200 | ␉␊ |
201 | ␉if (session != NULL && mustRelease) CFRelease(session);␊ |
202 | ␉␊ |
203 | ␉return err;␊ |
204 | }␊ |
205 | ␊ |
206 | +(NSMutableArray*)␉extractInfoWithBSDNames: (NSArray*) bsdNames␊ |
207 | {␊ |
208 | ␉return [PartitionInfoElement extractInfoWithBSDNames:bsdNames withArray: nil];␊ |
209 | }␊ |
210 | ␊ |
211 | /// main extraction method create a dictionary of PartitionInfoElement objects containing all needed information from disk arbitration API␊ |
212 | + (NSMutableArray*) extractInfoWithBSDNames: (NSArray*) names withArray:(NSMutableArray*) arr␊ |
213 | {␉␉␊ |
214 | ␉NSArray* partArr = [PartitionInfoElement createBSDPartitionList];␊ |
215 | ␉if (arr == nil)␊ |
216 | ␉␉arr = [[[NSMutableArray alloc ] init] autorelease];␊ |
217 | ␉␊ |
218 | ␉if (partArr!=nil && [partArr count]>0)␊ |
219 | ␉{␊ |
220 | ␉␉␉DASessionRef session = DASessionCreate(NULL);␊ |
221 | ␉␉␉for (NSString* part in partArr)␊ |
222 | ␉␉␉{␊ |
223 | ␉␉␉␉PartitionInfoElement* elt = ␊ |
224 | ␉␉␉␉[[[PartitionInfoElement alloc] initWithBSDName: part withinSession: session] autorelease];␊ |
225 | ␉␉␉␉if (elt!=nil && [[elt vName] length] >0 && [elt isValid] ) ␊ |
226 | ␉␉␉␉␉[arr addObject: elt];␊ |
227 | ␉␉␉}␊ |
228 | ␉␉␉if (session!=nil) CFRelease(session);␊ |
229 | ␉}␊ |
230 | ␉return arr;␊ |
231 | }␊ |
232 | ␊ |
233 | - (int) extractInfoWithBSDName: (NSString*) name ␊ |
234 | {␊ |
235 | ␉return [self extractInfoWithBSDName:name withinSession: nil]; ␊ |
236 | }␊ |
237 | ␊ |
238 | -(bool) isBootable␊ |
239 | {␊ |
240 | ␉bool bootable = false;␊ |
241 | ␉NSFileManager* mgr = [NSFileManager defaultManager];␊ |
242 | ␉NSString *fmt = @"/Volumes/%@%s";␊ |
243 | ␉NSRange r = [vPath rangeOfString:@"file://localhost/Volumes/"];␊ |
244 | ␉NSString * vol = [NSString stringWithFormat: @"%@/",vName];␊ |
245 | ␉␊ |
246 | ␉// ␊ |
247 | ␉if (r.location != NSNotFound) {␊ |
248 | ␉␉vol = [[vPath substringFromIndex:r.location+r.length] stringByReplacingOccurrencesOfString:@"%20" withString:@" "];␊ |
249 | ␉}␊ |
250 | ␉␊ |
251 | ␉// that Windows is the name of WIN32 bootable disk dir ...␊ |
252 | ␉if(␊ |
253 | ␉␉[mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "System/Library/Extensions" ]] ||␊ |
254 | ␉ [mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "ntldr" ]] ||␊ |
255 | ␉␉[mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "bootmgr" ]] ||␊ |
256 | ␉␉[mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "boot/bcd" ]] ||␊ |
257 | ␉␉[mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "pagefile.sys" ]] ||␊ |
258 | ␉␉[mgr fileExistsAtPath: [NSString stringWithFormat: fmt, vol, "hiberfil.sys" ]] ␊ |
259 | ␉ )␊ |
260 | ␉␉bootable=true;␊ |
261 | ␉else if ([vName rangeOfString:@"ext"].location != NSNotFound) // linux ?␊ |
262 | ␉␉bootable = true;␊ |
263 | ␉return bootable;␊ |
264 | }␊ |
265 | ␊ |
266 | /// Return the partition image index according to its file system␊ |
267 | -(int) imageIndexFromFs␊ |
268 | {␊ |
269 | ␉␊ |
270 | ␉if ( [[self vKind] rangeOfString:@"hfs"].location != NSNotFound )␊ |
271 | ␉␉return 0; // Mac␊ |
272 | ␉if ( [[self vKind] rangeOfString:@"fat"].location != NSNotFound ||␊ |
273 | ␉␉ [[self vKind] rangeOfString:@"ntfs"].location != NSNotFound ||␊ |
274 | ␉␉ [[self vKind] rangeOfString:@"dos"].location != NSNotFound )␊ |
275 | ␉␉return 1; // Windows␊ |
276 | ␉if ( [[self vKind] rangeOfString:@"ext"].location != NSNotFound )␊ |
277 | ␉␉return 2; // Linux␊ |
278 | ␉␊ |
279 | ␉return 10; // unknown␊ |
280 | }␊ |
281 | ␊ |
282 | /// Volume label trimming utility function␊ |
283 | +(NSString*) removesSpacesFromLabel:(NSString*) label␊ |
284 | {␊ |
285 | ␉if (label == nil || [label length]==0) return label;␊ |
286 | ␉return [label stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];␊ |
287 | }␊ |
288 | ␊ |
289 | /// make the object description available for NSLog() debug purpose␊ |
290 | -(NSString*) description␊ |
291 | {␊ |
292 | ␉NSString* format =␊ |
293 | ␉@"(\n"␊ |
294 | ␉ " bsdName %@\n deviceProtocol %@\n deviceInternal %i\n"␊ |
295 | ␉ " volumeName %@\n volumeKind %@\n volumeUUID %@\n"␊ |
296 | ␉ " volumePath %@\n mediaPath %@\n mediaRemovable %i\n"␉␊ |
297 | ␉ ")";␊ |
298 | ␉NSString* value = [NSString stringWithFormat: format,␊ |
299 | ␉␉␉␉␉ self.bsdName, self.devProtocol, self.devInternal, ␊ |
300 | ␉␉␉␉␉ self.vName, self.vKind, self.vUUID, ␊ |
301 | ␉␉␉␉␉ self.vPath, self.mediaPath, self.mediaRemovable ];␊ |
302 | ␉return value;␊ |
303 | }␊ |
304 | ␊ |
305 | ␊ |
306 | /// initialize a partition element with DA partition info␊ |
307 | -(id) initWithBSDName:(NSString*) name ␊ |
308 | {␊ |
309 | ␉self = [super init];␊ |
310 | ␉diskNum = partNum = -1;␊ |
311 | ␉err = [self extractInfoWithBSDName: name withinSession: nil];␊ |
312 | ␉return self;␊ |
313 | }␊ |
314 | ␊ |
315 | /// initialize a partition element with DA partition info and an optional opened session␊ |
316 | -(id) initWithBSDName:(NSString*) name withinSession:(DASessionRef) session␊ |
317 | {␊ |
318 | ␉self = [super init];␊ |
319 | ␉diskNum = partNum = -1;␊ |
320 | ␉err = [self extractInfoWithBSDName: name withinSession: session];␊ |
321 | ␉␊ |
322 | ␉return self;␊ |
323 | }␊ |
324 | ␊ |
325 | -(void) cleanup␊ |
326 | {␊ |
327 | ␊ |
328 | ␉if (descDict != nil) CFRelease(descDict);␊ |
329 | ␉if (bsdName != nil) [bsdName release];␊ |
330 | ␉if (vUUID != nil) [vUUID release];␊ |
331 | ␉if (vKind != nil) [vKind release];␊ |
332 | ␉if (vName != nil) [vName release];␊ |
333 | ␉if (mediaPath != nil) [mediaPath release];␊ |
334 | ␉if (devProtocol != nil) [devProtocol release];␊ |
335 | ␉if (vAliasName != nil) [vAliasName release];␊ |
336 | ␉bsdName = vUUID = vKind = vName = mediaPath = devProtocol = vAliasName = nil;␊ |
337 | ␉descDict = nil;␊ |
338 | }␊ |
339 | ␊ |
340 | /// release created objects␊ |
341 | -(void) dealloc␊ |
342 | {␊ |
343 | ␉[self cleanup];␊ |
344 | ␉[super dealloc];␊ |
345 | }␊ |
346 | ␊ |
347 | @end␊ |
348 | |