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