1 | /*␊ |
2 | * boot1-install.c␊ |
3 | * boot1-install␊ |
4 | *␊ |
5 | * Created by Zenith432 on November 19th, 2014.␊ |
6 | * Copyright (c) 2014 Zenith432. All rights reserved.␊ |
7 | *␊ |
8 | * Modified December 5th, 2014 by Micky1979: Added -u option to force umount.␊ |
9 | */␊ |
10 | ␊ |
11 | #include <assert.h>␊ |
12 | #include <stdio.h>␊ |
13 | #include <stdlib.h>␊ |
14 | #include <string.h>␊ |
15 | ␊ |
16 | #include <errno.h>␊ |
17 | #include <fcntl.h>␊ |
18 | #include <sys/stat.h>␊ |
19 | #include <sys/types.h>␊ |
20 | #include <sys/uio.h>␊ |
21 | #include <unistd.h>␊ |
22 | ␊ |
23 | #include <CoreFoundation/CoreFoundation.h>␊ |
24 | #include <DiskArbitration/DiskArbitration.h>␊ |
25 | ␊ |
26 | struct buffer_t␊ |
27 | {␊ |
28 | ␉unsigned char* _b;␊ |
29 | ␉size_t _s;␊ |
30 | };␊ |
31 | ␊ |
32 | enum volume_kind_t␊ |
33 | {␊ |
34 | ␉_undetected = 0,␊ |
35 | ␉_exfat = 1,␊ |
36 | ␉_hfs = 2,␊ |
37 | ␉_msdos = 3,␊ |
38 | ␉_other = 255␊ |
39 | };␊ |
40 | ␊ |
41 | static int isVolumeMounted = 0;␊ |
42 | static int isMediaWhole = 0;␊ |
43 | static int isMediaLeaf = 0;␊ |
44 | static enum volume_kind_t daVolumeKind = _undetected;␊ |
45 | ␊ |
46 | static struct buffer_t bpbBlob = { NULL, 0 };␊ |
47 | static struct buffer_t bootBlob = { NULL, 0 };␊ |
48 | static struct buffer_t outputBlob = { NULL, 0 };␊ |
49 | ␊ |
50 | static char const UnsupportedMessage[] = "Only exFAT, FAT32 or HFS+ volumes are supported\n";␊ |
51 | static char const exfatID[] = "EXFAT ";␊ |
52 | static char const fat32ID[] = "FAT32 ";␊ |
53 | static char const devrdisk[] = "/dev/rdisk";␊ |
54 | static char const devdisk[] = "/dev/disk";␊ |
55 | static char const defaultBootFile_exfat[] = "./boot1x";␊ |
56 | static char const defaultBootFile_hfs[] = "./boot1h";␊ |
57 | static char const defaultBootFile_fat32[] = "./boot1f32";␊ |
58 | ␊ |
59 | static __used char const copyright[] = "Copyright 2014 Zenith432";␊ |
60 | ␊ |
61 | static int checkExfat(struct buffer_t const*);␊ |
62 | static int checkFat32(struct buffer_t const*);␊ |
63 | static int loadChunk(char const*, off_t, off_t, struct buffer_t*);␊ |
64 | static void unsupported(void);␊ |
65 | ␊ |
66 | #pragma mark -␊ |
67 | #pragma mark Cleaners␊ |
68 | #pragma mark -␊ |
69 | ␊ |
70 | static␊ |
71 | void free_buffer(struct buffer_t* pBuffer)␊ |
72 | {␊ |
73 | ␉assert(pBuffer);␊ |
74 | ␉if (pBuffer->_b) {␊ |
75 | ␉␉free(pBuffer->_b);␊ |
76 | ␉␉pBuffer->_b = NULL;␊ |
77 | ␉␉pBuffer->_s = 0;␊ |
78 | ␉}␊ |
79 | }␊ |
80 | ␊ |
81 | /*␊ |
82 | * Uses statics␊ |
83 | */␊ |
84 | static␊ |
85 | void cleanup(void)␊ |
86 | {␊ |
87 | ␉free_buffer(&outputBlob);␊ |
88 | ␉free_buffer(&bootBlob);␊ |
89 | ␉free_buffer(&bpbBlob);␊ |
90 | }␊ |
91 | ␊ |
92 | #pragma mark -␊ |
93 | #pragma mark ExFAT Processor␊ |
94 | #pragma mark -␊ |
95 | ␊ |
96 | static␊ |
97 | unsigned VBRChecksum(unsigned char const* octets, size_t NumberOfBytes)␊ |
98 | {␊ |
99 | ␉unsigned Checksum = 0;␊ |
100 | ␉size_t Index;␊ |
101 | ␉for (Index = 0; Index != NumberOfBytes; ++Index)␊ |
102 | ␉{␊ |
103 | ␉␉if (Index == 106 || Index == 107 || Index == 112)␊ |
104 | ␉␉␉continue;␊ |
105 | ␉␉Checksum = ((Checksum << 31) | (Checksum >> 1)) + (unsigned) octets[Index];␊ |
106 | ␉}␊ |
107 | ␉return Checksum;␊ |
108 | }␊ |
109 | ␊ |
110 | static␊ |
111 | int calcSum(struct buffer_t const* pBootBlob,␊ |
112 | ␉␉␉struct buffer_t const* pBpbBlob,␊ |
113 | ␉␉␉struct buffer_t* pOutputBlob,␊ |
114 | ␉␉␉char const* pathName)␊ |
115 | {␊ |
116 | ␉unsigned char *outBuffer, *p, *q;␊ |
117 | ␉size_t outSize, toCopy, leftOver;␊ |
118 | ␉unsigned Checksum;␊ |
119 | ␊ |
120 | ␉assert(pBootBlob && pBpbBlob);␊ |
121 | ␉if (pBootBlob->_s > 9U * 512U) {␊ |
122 | ␉␉fprintf(stderr, "Boot Code must be at most 4608 bytes\n");␊ |
123 | ␉␉return -1;␊ |
124 | ␉}␊ |
125 | ␉if (pBpbBlob->_s < 113U) {␊ |
126 | ␉␉fprintf(stderr, "BPB must be at least 113 bytes\n");␊ |
127 | ␉␉return -1;␊ |
128 | ␉}␊ |
129 | ␉if (!checkExfat(pBpbBlob)) {␊ |
130 | ␉␉fprintf(stderr, "BPB does not contain proper exFAT signature\n");␊ |
131 | ␉␉return -1;␊ |
132 | ␉}␊ |
133 | ␉outSize = 12U * 512U;␊ |
134 | ␉outBuffer = malloc(outSize);␊ |
135 | ␉if (!outBuffer) {␊ |
136 | ␉␉fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__);␊ |
137 | ␉␉return -1;␊ |
138 | ␉}␊ |
139 | ␉memset(outBuffer, 0, outSize);␊ |
140 | ␉memcpy(outBuffer, pBootBlob->_b, pBootBlob->_s);␊ |
141 | ␉memcpy(&outBuffer[3], &pBpbBlob->_b[3], 8);␊ |
142 | ␉memset(&outBuffer[11], 0, 53);␊ |
143 | ␉toCopy = 120;␊ |
144 | ␉if (pBpbBlob->_s < toCopy)␊ |
145 | ␉␉toCopy = pBpbBlob->_s;␊ |
146 | ␉leftOver = 120 - toCopy;␊ |
147 | ␉memcpy(&outBuffer[64], &pBpbBlob->_b[64], toCopy - 64);␊ |
148 | ␉if (leftOver)␊ |
149 | ␉␉memset(&outBuffer[120 - leftOver], 0, leftOver);␊ |
150 | ␉for (toCopy = 0; toCopy != 9; ++toCopy) {␊ |
151 | ␉␉p = outBuffer + toCopy * 512U + 508U;␊ |
152 | ␉␉p[2] = 0x55U;␊ |
153 | ␉␉p[3] = 0xAAU;␊ |
154 | ␉␉if (toCopy) {␊ |
155 | ␉␉␉p[0] = 0U;␊ |
156 | ␉␉␉p[1] = 0U;␊ |
157 | ␉␉}␊ |
158 | ␉}␊ |
159 | ␉if (pathName) {␊ |
160 | ␉␉/*␊ |
161 | ␉␉ * Copy OEM Parameters record␊ |
162 | ␉␉ */␊ |
163 | ␉␉struct buffer_t auxBlob = { NULL, 0 };␊ |
164 | ␉␉if (loadChunk(pathName, 9 * 512 , 512, &auxBlob) >= 0) {␊ |
165 | ␉␉␉memcpy(&outBuffer[9 * 512], &auxBlob._b[0], 512);␊ |
166 | ␉␉␉free_buffer(&auxBlob);␊ |
167 | ␉␉}␊ |
168 | ␉}␊ |
169 | ␉Checksum = VBRChecksum(outBuffer, 11U * 512U);␊ |
170 | ␉p = outBuffer + 11U * 512U;␊ |
171 | ␉q = p + 512U;␊ |
172 | ␉for (; p < q; p += 4) {␊ |
173 | ␉␉*(unsigned*) p = Checksum;␊ |
174 | ␉}␊ |
175 | ␉if (pOutputBlob) {␊ |
176 | ␉␉pOutputBlob->_b = outBuffer;␊ |
177 | ␉␉pOutputBlob->_s = outSize;␊ |
178 | ␉} else␊ |
179 | ␉␉free(outBuffer);␊ |
180 | ␉return 0;␊ |
181 | }␊ |
182 | ␊ |
183 | #pragma mark -␊ |
184 | #pragma mark FAT32 Processor␊ |
185 | #pragma mark -␊ |
186 | ␊ |
187 | static␊ |
188 | int fat32Layout(struct buffer_t const* pBootBlob,␊ |
189 | ␉␉␉␉struct buffer_t const* pBpbBlob,␊ |
190 | ␉␉␉␉struct buffer_t* pOutputBlob)␊ |
191 | {␊ |
192 | ␉unsigned char *outBuffer;␊ |
193 | ␉size_t outSize;␊ |
194 | ␊ |
195 | ␉assert(pBootBlob && pBpbBlob);␊ |
196 | ␉if (pBootBlob->_s > 512U) {␊ |
197 | ␉␉fprintf(stderr, "Boot Code must be at most 512 bytes\n");␊ |
198 | ␉␉return -1;␊ |
199 | ␉}␊ |
200 | ␉if (pBpbBlob->_s < 90U) {␊ |
201 | ␉␉fprintf(stderr, "BPB must be at least 90 bytes\n");␊ |
202 | ␉␉return -1;␊ |
203 | ␉}␊ |
204 | ␉if (!checkFat32(pBpbBlob)) {␊ |
205 | ␉␉fprintf(stderr, "BPB does not contain proper FAT32 signature\n");␊ |
206 | ␉␉return -1;␊ |
207 | ␉}␊ |
208 | ␉outSize = 512U;␊ |
209 | ␉outBuffer = malloc(outSize);␊ |
210 | ␉if (!outBuffer) {␊ |
211 | ␉␉fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__);␊ |
212 | ␉␉return -1;␊ |
213 | ␉}␊ |
214 | ␉memset(outBuffer, 0, outSize);␊ |
215 | ␉memcpy(outBuffer, pBootBlob->_b, pBootBlob->_s);␊ |
216 | ␉memcpy(&outBuffer[3], &pBpbBlob->_b[3], 87);␊ |
217 | ␉outBuffer[510] = 0x55U;␊ |
218 | ␉outBuffer[511] = 0xAAU;␊ |
219 | ␉if (pOutputBlob) {␊ |
220 | ␉␉pOutputBlob->_b = outBuffer;␊ |
221 | ␉␉pOutputBlob->_s = outSize;␊ |
222 | ␉} else␊ |
223 | ␉␉free(outBuffer);␊ |
224 | ␉return 0;␊ |
225 | }␊ |
226 | ␊ |
227 | #pragma mark -␊ |
228 | #pragma mark File Operations␊ |
229 | #pragma mark -␊ |
230 | ␊ |
231 | static␊ |
232 | void writeVBR(char const* pathName,␊ |
233 | ␉␉␉ struct buffer_t const* pBuffer,␊ |
234 | ␉␉␉ int numCopies,␊ |
235 | ␉␉␉ size_t expectedSize,␊ |
236 | ␉␉␉ char const* volumeType)␊ |
237 | {␊ |
238 | ␉int fd, j;␊ |
239 | ␊ |
240 | ␉assert(pathName && pBuffer && volumeType);␊ |
241 | ␉if (pBuffer->_s != expectedSize) {␊ |
242 | ␉␉fprintf(stderr, "Unexpected %s VBR size %lu (expected %lu)\n", volumeType, pBuffer->_s, expectedSize);␊ |
243 | ␉␉return;␊ |
244 | ␉}␊ |
245 | ␉fd = open(pathName, O_WRONLY);␊ |
246 | ␉if (fd < 0) {␊ |
247 | ␉␉fprintf(stderr, "Unable to write boot record to %s, %s\n", pathName, strerror(errno));␊ |
248 | ␉}␊ |
249 | ␉for (j = 0; j != numCopies; ++j)␊ |
250 | ␉␉write(fd, pBuffer->_b, pBuffer->_s);␊ |
251 | ␉close(fd);␊ |
252 | }␊ |
253 | ␊ |
254 | static␊ |
255 | int loadChunk(char const* pathName, off_t startOffset, off_t bytesToRead, struct buffer_t* pBuffer)␊ |
256 | {␊ |
257 | ␉int fd;␊ |
258 | ␉ssize_t rc;␊ |
259 | ␉unsigned char* p;␊ |
260 | ␉struct stat buf;␊ |
261 | ␊ |
262 | ␉assert(pathName);␊ |
263 | ␉fd = open(pathName, O_RDONLY);␊ |
264 | ␉if (fd < 0) {␊ |
265 | ␉␉fprintf(stderr, "Unable to open %s, %s\n", pathName, strerror(errno));␊ |
266 | ␉␉return -1;␊ |
267 | ␉}␊ |
268 | ␉if (bytesToRead > 0)␊ |
269 | ␉␉buf.st_size = bytesToRead;␊ |
270 | ␉else if (fstat(fd, &buf) < 0) {␊ |
271 | ␉␉fprintf(stderr, "Unable to fstat %s, %s\n", pathName, strerror(errno));␊ |
272 | ␉␉close(fd);␊ |
273 | ␉␉return -1;␊ |
274 | ␉}␊ |
275 | ␉if (startOffset > 0) {␊ |
276 | ␉␉off_t t = lseek(fd, startOffset, SEEK_SET);␊ |
277 | ␉␉if (t < 0) {␊ |
278 | ␉␉␉fprintf(stderr, "Unable to lseek %s, %s\n", pathName, strerror(errno));␊ |
279 | ␉␉␉close(fd);␊ |
280 | ␉␉␉return -1;␊ |
281 | ␉␉}␊ |
282 | ␉␉if (t != startOffset) {␊ |
283 | ␉␉␉fprintf(stderr, "lseek %s returned wrong value %lld instead of %lld\n", pathName, t, startOffset);␊ |
284 | ␉␉␉close(fd);␊ |
285 | ␉␉␉return -1;␊ |
286 | ␉␉}␊ |
287 | ␉␉if (bytesToRead <= 0)␊ |
288 | ␉␉␉buf.st_size -= t;␊ |
289 | ␉}␊ |
290 | ␉p = malloc((size_t) buf.st_size);␊ |
291 | ␉if (!p) {␊ |
292 | ␉␉fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__);␊ |
293 | ␉␉close(fd);␊ |
294 | ␉␉return -1;␊ |
295 | ␉}␊ |
296 | ␉rc = read(fd, p, (size_t) buf.st_size);␊ |
297 | ␉if (rc < 0) {␊ |
298 | ␉␉fprintf(stderr, "Unable to read from %s, %s\n", pathName, strerror(errno));␊ |
299 | ␉␉free(p);␊ |
300 | ␉␉close(fd);␊ |
301 | ␉␉return -1;␊ |
302 | ␉}␊ |
303 | ␉close(fd);␊ |
304 | ␉if (rc != buf.st_size) {␊ |
305 | ␉␉fprintf(stderr, "Unable to read entire chunk from %s, read %ld/%lld\n", pathName, rc, buf.st_size);␊ |
306 | ␉␉free(p);␊ |
307 | ␉␉return -1;␊ |
308 | ␉}␊ |
309 | ␉if (pBuffer) {␊ |
310 | ␉␉pBuffer->_b = p;␊ |
311 | ␉␉pBuffer->_s = (size_t) rc;␊ |
312 | ␉} else␊ |
313 | ␉␉free(p);␊ |
314 | ␉return 0;␊ |
315 | }␊ |
316 | ␊ |
317 | #pragma mark -␊ |
318 | #pragma mark DiskArbitration Helpers␊ |
319 | #pragma mark -␊ |
320 | ␊ |
321 | static␊ |
322 | char const* toBSDName(char const* pathName)␊ |
323 | {␊ |
324 | ␉assert(pathName);␊ |
325 | ␉return strncmp(pathName, &devrdisk[0], 10) ? pathName : &pathName[6];␊ |
326 | }␊ |
327 | ␊ |
328 | static␊ |
329 | char const* daReturnStr(DAReturn v)␊ |
330 | {␊ |
331 | ␉if (unix_err(err_get_code(v)) == v)␊ |
332 | ␉␉return strerror(err_get_code(v));␊ |
333 | ␉switch (v) {␊ |
334 | ␉␉case kDAReturnError:␊ |
335 | ␉␉␉return "Error";␊ |
336 | ␉␉case kDAReturnBusy:␊ |
337 | ␉␉␉return "Busy";␊ |
338 | ␉␉case kDAReturnBadArgument:␊ |
339 | ␉␉␉return "Bad Argument";␊ |
340 | ␉␉case kDAReturnExclusiveAccess:␊ |
341 | ␉␉␉return "Exclusive Access";␊ |
342 | ␉␉case kDAReturnNoResources:␊ |
343 | ␉␉␉return "No Resources";␊ |
344 | ␉␉case kDAReturnNotFound:␊ |
345 | ␉␉␉return "Not Found";␊ |
346 | ␉␉case kDAReturnNotMounted:␊ |
347 | ␉␉␉return "Not Mounted";␊ |
348 | ␉␉case kDAReturnNotPermitted:␊ |
349 | ␉␉␉return "Not Permitted";␊ |
350 | ␉␉case kDAReturnNotPrivileged:␊ |
351 | ␉␉␉return "Not Privileged";␊ |
352 | ␉␉case kDAReturnNotReady:␊ |
353 | ␉␉␉return "Not Ready";␊ |
354 | ␉␉case kDAReturnNotWritable:␊ |
355 | ␉␉␉return "Not Writable";␊ |
356 | ␉␉case kDAReturnUnsupported:␊ |
357 | ␉␉␉return "Unsupported";␊ |
358 | ␉␉default:␊ |
359 | ␉␉␉return "Unknown";␊ |
360 | ␉}␊ |
361 | }␊ |
362 | ␊ |
363 | static␊ |
364 | int getDASessionAndDisk(char const* pathName, DASessionRef* pSession, DADiskRef* pDisk)␊ |
365 | {␊ |
366 | ␉DASessionRef session;␊ |
367 | ␉DADiskRef disk;␊ |
368 | ␊ |
369 | ␉assert(pathName);␊ |
370 | ␉session = DASessionCreate(kCFAllocatorDefault);␊ |
371 | ␉if (!session) {␊ |
372 | ␉␉fprintf(stderr, "DASessionCreate returned NULL\n");␊ |
373 | ␉␉return -1;␊ |
374 | ␉}␊ |
375 | ␉disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, toBSDName(pathName));␊ |
376 | ␉if (!disk) {␊ |
377 | ␉␉CFRelease(session);␊ |
378 | ␉␉fprintf(stderr, "DADiskCreateFromBSDName(%s) returned NULL\n", pathName);␊ |
379 | ␉␉return -1;␊ |
380 | ␉}␊ |
381 | ␉if (pDisk)␊ |
382 | ␉␉*pDisk = disk;␊ |
383 | ␉else␊ |
384 | ␉␉CFRelease(disk);␊ |
385 | ␉if (pSession)␊ |
386 | ␉␉*pSession = session;␊ |
387 | ␉else␊ |
388 | ␉␉CFRelease(session);␊ |
389 | ␉return 0;␊ |
390 | }␊ |
391 | ␊ |
392 | #pragma mark -␊ |
393 | #pragma mark Mount/UMount␊ |
394 | #pragma mark -␊ |
395 | ␊ |
396 | static␊ |
397 | void umountCallback(DADiskRef disk __unused,␊ |
398 | ␉␉␉␉␉DADissenterRef dissenter,␊ |
399 | ␉␉␉␉␉void *context)␊ |
400 | {␊ |
401 | ␉if (context && dissenter != NULL) {␊ |
402 | ␉␉*(int*) context = -1;␊ |
403 | ␉␉fprintf(stderr, "umount unsuccessful, status %s\n", daReturnStr(DADissenterGetStatus(dissenter)));␊ |
404 | ␉}␊ |
405 | ␉CFRunLoopStop(CFRunLoopGetCurrent());␊ |
406 | }␊ |
407 | ␊ |
408 | static␊ |
409 | int umount(char const* pathName, int forceUmount)␊ |
410 | {␊ |
411 | ␉DASessionRef session;␊ |
412 | ␉DADiskRef disk;␊ |
413 | ␉int rc;␊ |
414 | ␊ |
415 | ␉assert(pathName);␊ |
416 | ␉if (getDASessionAndDisk(pathName, &session, &disk) < 0)␊ |
417 | ␉␉return -1;␊ |
418 | ␉rc = 0;␊ |
419 | ␉DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);␊ |
420 | ␉DADiskUnmount(disk, forceUmount ? kDADiskUnmountOptionForce : kDADiskUnmountOptionDefault, umountCallback, &rc);␊ |
421 | ␉CFRunLoopRun();␊ |
422 | ␉DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);␊ |
423 | ␉CFRelease(disk);␊ |
424 | ␉CFRelease(session);␊ |
425 | ␉return rc;␊ |
426 | }␊ |
427 | ␊ |
428 | static␊ |
429 | void mountCallback(DADiskRef disk __unused,␊ |
430 | ␉␉␉␉ DADissenterRef dissenter,␊ |
431 | ␉␉␉␉ void *context)␊ |
432 | {␊ |
433 | ␉if (context && dissenter != NULL) {␊ |
434 | ␉␉*(int*) context = -1;␊ |
435 | ␉␉fprintf(stderr, "mount unsuccessful, status %s\n", daReturnStr(DADissenterGetStatus(dissenter)));␊ |
436 | ␉}␊ |
437 | ␉CFRunLoopStop(CFRunLoopGetCurrent());␊ |
438 | }␊ |
439 | ␊ |
440 | static␊ |
441 | int mount(char const* pathName)␊ |
442 | {␊ |
443 | ␉DASessionRef session;␊ |
444 | ␉DADiskRef disk;␊ |
445 | ␉int rc;␊ |
446 | ␊ |
447 | ␉assert(pathName);␊ |
448 | ␉if (getDASessionAndDisk(pathName, &session, &disk) < 0)␊ |
449 | ␉␉return -1;␊ |
450 | ␉rc = 0;␊ |
451 | ␉DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);␊ |
452 | ␉DADiskMount(disk, NULL, kDADiskMountOptionDefault, mountCallback, &rc);␊ |
453 | ␉CFRunLoopRun();␊ |
454 | ␉DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);␊ |
455 | ␉CFRelease(disk);␊ |
456 | ␉CFRelease(session);␊ |
457 | ␉return rc;␊ |
458 | }␊ |
459 | ␊ |
460 | #pragma mark -␊ |
461 | #pragma mark Analyze Volume␊ |
462 | #pragma mark -␊ |
463 | ␊ |
464 | static␊ |
465 | int checkExfat(struct buffer_t const* pBpbBlob)␊ |
466 | {␊ |
467 | ␉assert(pBpbBlob);␊ |
468 | ␉return !memcmp(&pBpbBlob->_b[3], &exfatID[0], 8);␊ |
469 | }␊ |
470 | ␊ |
471 | static␊ |
472 | int checkHFS(struct buffer_t const* pBpbBlob)␊ |
473 | {␊ |
474 | ␉uint16_t sig;␊ |
475 | ␊ |
476 | ␉assert(pBpbBlob);␊ |
477 | ␉sig = OSSwapBigToHostInt16(*(uint16_t const*)&pBpbBlob->_b[0]);␊ |
478 | ␉return sig == 0x4244 || sig == 0x482B || sig == 0x4858;␉␉/* 'BD', 'H+', 'HX' */␊ |
479 | }␊ |
480 | ␊ |
481 | static␊ |
482 | int checkFat32(struct buffer_t const* pBpbBlob)␊ |
483 | {␊ |
484 | ␉uint16_t bytesPerSector, rootEntCnt;␊ |
485 | ␉uint8_t sectorsPerCluster;␊ |
486 | ␊ |
487 | ␉assert(pBpbBlob);␊ |
488 | ␉bytesPerSector = OSSwapLittleToHostInt16(*(uint16_t const*)&pBpbBlob->_b[11]);␊ |
489 | ␉if ((bytesPerSector & (bytesPerSector - 1U)) ||␊ |
490 | ␉␉bytesPerSector < 0x200U ||␊ |
491 | ␉␉bytesPerSector > 0x1000U)␊ |
492 | ␉␉return 0;␊ |
493 | ␉sectorsPerCluster = pBpbBlob->_b[13];␊ |
494 | ␉if (!sectorsPerCluster ||␊ |
495 | ␉␉(sectorsPerCluster & (sectorsPerCluster - 1U)))␊ |
496 | ␉␉return 0;␊ |
497 | ␉rootEntCnt = OSSwapLittleToHostInt16(*(uint16_t const*)&pBpbBlob->_b[17]);␊ |
498 | ␉if (rootEntCnt)␊ |
499 | ␉␉return 0;␊ |
500 | ␉return !memcmp(&pBpbBlob->_b[82], &fat32ID[0], 8);␊ |
501 | }␊ |
502 | ␊ |
503 | static␊ |
504 | int checkSupportedVolume(enum volume_kind_t* pKind, struct buffer_t const* pBpbBlob, char const* pathName)␊ |
505 | {␊ |
506 | ␉int rc;␊ |
507 | ␊ |
508 | ␉assert(pKind && pBpbBlob);␊ |
509 | ␉rc = -1;␊ |
510 | ␉switch (*pKind) {␊ |
511 | ␉␉case _undetected:␊ |
512 | ␉␉␉if (checkExfat(pBpbBlob)) {␊ |
513 | ␉␉␉␉*pKind = _exfat;␊ |
514 | ␉␉␉␉rc = 0;␊ |
515 | ␉␉␉} else if (checkFat32(pBpbBlob)) {␊ |
516 | ␉␉␉␉*pKind = _msdos;␊ |
517 | ␉␉␉␉rc = 0;␊ |
518 | ␉␉␉} else if (pathName) {␊ |
519 | ␉␉␉␉struct buffer_t auxBlob = { NULL, 0 };␊ |
520 | ␉␉␉␉if (loadChunk(pathName, 1024 , 512, &auxBlob) >= 0) {␊ |
521 | ␉␉␉␉␉if (checkHFS(&auxBlob)) {␊ |
522 | ␉␉␉␉␉␉*pKind = _hfs;␊ |
523 | ␉␉␉␉␉␉rc = 0;␊ |
524 | ␉␉␉␉␉}␊ |
525 | ␉␉␉␉␉free_buffer(&auxBlob);␊ |
526 | ␉␉␉␉}␊ |
527 | ␉␉␉}␊ |
528 | ␉␉␉break;␊ |
529 | ␉␉case _exfat:␊ |
530 | ␉␉␉if (checkExfat(pBpbBlob))␊ |
531 | ␉␉␉␉rc = 0;␊ |
532 | ␉␉␉else␊ |
533 | ␉␉␉␉*pKind = _other;␊ |
534 | ␉␉␉break;␊ |
535 | ␉␉case _hfs:␊ |
536 | ␉␉␉if (checkHFS(pBpbBlob))␊ |
537 | ␉␉␉␉rc = 0;␊ |
538 | ␉␉␉else␊ |
539 | ␉␉␉␉*pKind = _other;␊ |
540 | ␉␉␉break;␊ |
541 | ␉␉case _msdos:␊ |
542 | ␉␉␉if (checkFat32(pBpbBlob))␊ |
543 | ␉␉␉␉rc = 0;␊ |
544 | ␉␉␉else␊ |
545 | ␉␉␉␉*pKind = _other;␊ |
546 | ␉␉␉break;␊ |
547 | ␉␉default:␊ |
548 | ␉␉␉break;␊ |
549 | ␉}␊ |
550 | ␉if (rc < 0)␊ |
551 | ␉␉unsupported();␊ |
552 | ␉return rc;␊ |
553 | }␊ |
554 | ␊ |
555 | /*␊ |
556 | * Uses statics␊ |
557 | */␊ |
558 | static␊ |
559 | int checkDevicePath2(char const* pathName)␊ |
560 | {␊ |
561 | ␉DASessionRef session;␊ |
562 | ␉DADiskRef disk;␊ |
563 | ␉CFDictionaryRef descDict;␊ |
564 | ␉CFStringRef s_ref;␊ |
565 | ␉CFBooleanRef b_ref;␊ |
566 | ␊ |
567 | ␉assert(pathName);␊ |
568 | ␉if (getDASessionAndDisk(pathName, &session, &disk) < 0)␊ |
569 | ␉␉return -1;␊ |
570 | ␉descDict = DADiskCopyDescription(disk);␊ |
571 | ␉if (!descDict) {␊ |
572 | ␉␉CFRelease(disk);␊ |
573 | ␉␉CFRelease(session);␊ |
574 | ␉␉fprintf(stderr, "DADiskCopyDescription(%s) returned NULL\n", pathName);␊ |
575 | ␉␉return -1;␊ |
576 | ␉}␊ |
577 | ␉if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionMediaWholeKey, (void const**) &b_ref) &&␊ |
578 | ␉␉CFBooleanGetValue(b_ref))␊ |
579 | ␉␉isMediaWhole = 1;␊ |
580 | ␉if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionMediaLeafKey, (void const**) &b_ref) &&␊ |
581 | ␉␉CFBooleanGetValue(b_ref))␊ |
582 | ␉␉isMediaLeaf = 1;␊ |
583 | ␉if (CFDictionaryContainsKey(descDict, kDADiskDescriptionVolumePathKey))␊ |
584 | ␉␉isVolumeMounted = 1;␊ |
585 | ␉if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionVolumeKindKey, (void const**) &s_ref)) {␊ |
586 | ␉␉static char cstr_buffer[64];␊ |
587 | ␉␉char const* cstr = CFStringGetCStringPtr(s_ref, kCFStringEncodingUTF8);␊ |
588 | ␉␉if (!cstr) {␊ |
589 | ␉␉␉CFStringGetCString(s_ref, &cstr_buffer[0], (CFIndex) sizeof cstr_buffer, kCFStringEncodingUTF8);␊ |
590 | ␉␉␉cstr = &cstr_buffer[0];␊ |
591 | ␉␉}␊ |
592 | #if 0␊ |
593 | ␉␉printf("DAVolumeKind %s\n", cstr);␊ |
594 | #endif␊ |
595 | ␉␉if (!strcmp(cstr, "exfat"))␊ |
596 | ␉␉␉daVolumeKind = _exfat;␊ |
597 | ␉␉else if (!strcmp(cstr, "hfs"))␊ |
598 | ␉␉␉daVolumeKind = _hfs;␊ |
599 | ␉␉else if (!strcmp(cstr, "msdos"))␊ |
600 | ␉␉␉daVolumeKind = _msdos;␊ |
601 | ␉␉else␊ |
602 | ␉␉␉daVolumeKind = _other;␊ |
603 | ␉}␊ |
604 | #if 0␊ |
605 | ␉printf(stderr, "whole %c, leaf %c, mounted %c\n",␊ |
606 | ␉␉ isMediaWhole ? 'Y' : 'N',␊ |
607 | ␉␉ isMediaLeaf ? 'Y' : 'N',␊ |
608 | ␉␉ isVolumeMounted ? 'Y' : 'N');␊ |
609 | #endif␊ |
610 | #if 0␊ |
611 | ␉CFShow(descDict);␊ |
612 | #endif␊ |
613 | ␉CFRelease(descDict);␊ |
614 | ␉CFRelease(disk);␊ |
615 | ␉CFRelease(session);␊ |
616 | ␉return 0;␊ |
617 | }␊ |
618 | ␊ |
619 | static␊ |
620 | int checkDevicePath(char const* pathName)␊ |
621 | {␊ |
622 | ␉struct stat buf;␊ |
623 | ␊ |
624 | ␉assert(pathName);␊ |
625 | ␉if (strncmp(pathName, &devdisk[0], 9) != 0 &&␊ |
626 | ␉␉strncmp(pathName, &devrdisk[0], 10) != 0) {␊ |
627 | ␉␉fprintf(stderr, "disk must be of form /dev/rdiskUsS or /dev/diskUsS\ndisk is %s\n", pathName);␊ |
628 | ␉␉return -1;␊ |
629 | ␉}␊ |
630 | ␉if (stat(pathName, &buf) < 0) {␊ |
631 | ␉␉fprintf(stderr, "stat on %s failed, %s\n", pathName, strerror(errno));␊ |
632 | ␉␉return -1;␊ |
633 | ␉}␊ |
634 | ␉if (!(buf.st_mode & (S_IFCHR | S_IFBLK))) {␊ |
635 | ␉␉fprintf(stderr, "%s is not a block or character special device\n", pathName);␊ |
636 | ␉␉return -1;␊ |
637 | ␉}␊ |
638 | ␉/*␊ |
639 | ␉ * FIXME: milk information from st_rdev - what's in it?␊ |
640 | ␉ */␊ |
641 | #if 0␊ |
642 | ␉printf("size of buf is %lu\n", sizeof buf);␊ |
643 | ␉printf("st_dev %#x\n", buf.st_dev);␊ |
644 | ␉printf("st_ino %llu\n", buf.st_ino);␊ |
645 | ␉printf("st_mode %#o\n", buf.st_mode);␊ |
646 | ␉printf("st_nlink %u\n", buf.st_nlink);␊ |
647 | ␉printf("st_uid %u\n", buf.st_uid);␊ |
648 | ␉printf("st_gid %u\n", buf.st_gid);␊ |
649 | ␉printf("st_rdev %#x\n", buf.st_rdev);␊ |
650 | ␉printf("st_size %llu\n", buf.st_size);␊ |
651 | ␉printf("st_blocks %llu\n", buf.st_blocks);␊ |
652 | ␉printf("st_blksize %u\n", buf.st_blksize);␊ |
653 | ␉printf("st_flags %#x\n", buf.st_flags);␊ |
654 | ␉printf("st_gen %u\n", buf.st_gen);␊ |
655 | #endif␊ |
656 | ␉return 0;␊ |
657 | }␊ |
658 | ␊ |
659 | #pragma mark -␊ |
660 | #pragma mark Usage␊ |
661 | #pragma mark -␊ |
662 | ␊ |
663 | static␊ |
664 | void usage(char const* self)␊ |
665 | {␊ |
666 | ␉assert(self);␊ |
667 | ␉fprintf(stderr, "Usage: %s [-yMu] [-f boot_code_file] disk\n", self);␊ |
668 | ␉fprintf(stderr, " boot_code_file is an optional boot template\n");␊ |
669 | ␉fprintf(stderr, " -y: don't ask any questions\n");␊ |
670 | ␉fprintf(stderr, " -M: keep volume mounted while proceeding (useful for root filesystem)\n");␊ |
671 | ␉fprintf(stderr, " -u: force umount (suppresses -M option if given)\n");␊ |
672 | ␉fprintf(stderr, "disk is of the form /dev/rdiskUsS or /dev/diskUsS\n");␊ |
673 | ␉fprintf(stderr, "default boot files are\n");␊ |
674 | ␉fprintf(stderr, " boot1h for HFS+\n");␊ |
675 | ␉fprintf(stderr, " boot1f32 for FAT32\n");␊ |
676 | ␉fprintf(stderr, " boot1x for exFAT\n");␊ |
677 | }␊ |
678 | ␊ |
679 | static␊ |
680 | void unsupported(void)␊ |
681 | {␊ |
682 | ␉fprintf(stderr, "%s", &UnsupportedMessage[0]);␊ |
683 | }␊ |
684 | ␊ |
685 | #pragma mark -␊ |
686 | #pragma mark Main␊ |
687 | #pragma mark -␊ |
688 | ␊ |
689 | int main(int argc, char* const argv[])␊ |
690 | {␊ |
691 | ␉int ch;␊ |
692 | ␉char const* bootFile = NULL;␊ |
693 | ␉char const* devicePath = NULL;␊ |
694 | ␉int dontAsk = 0;␊ |
695 | ␉int keepMounted = 0;␊ |
696 | ␉int forceUmount = 0;␊ |
697 | ␊ |
698 | ␉while ((ch = getopt(argc, argv, "yMuf:")) != -1)␊ |
699 | ␉␉switch (ch) {␊ |
700 | ␉␉␉case 'y':␊ |
701 | ␉␉␉␉dontAsk = 1;␊ |
702 | ␉␉␉␉break;␊ |
703 | ␉␉␉case 'M':␊ |
704 | ␉␉␉␉keepMounted = 1;␊ |
705 | ␉␉␉␉break;␊ |
706 | ␉␉␉case 'u':␊ |
707 | ␉␉␉␉forceUmount = 1;␊ |
708 | ␉␉␉␉break;␊ |
709 | ␉␉␉case 'f':␊ |
710 | ␉␉␉␉bootFile = optarg;␊ |
711 | ␉␉␉␉break;␊ |
712 | ␉␉␉default:␊ |
713 | ␉␉␉␉goto usage_and_error;␊ |
714 | ␉␉}␊ |
715 | ␉if (optind + 1 > argc)␊ |
716 | ␉␉goto usage_and_error;␊ |
717 | ␉devicePath = argv[optind];␊ |
718 | ␉if (geteuid() != 0) {␊ |
719 | ␉␉fprintf(stderr, "This program must be run as root\n");␊ |
720 | ␉␉return -1;␊ |
721 | ␉}␊ |
722 | ␉if (forceUmount)␊ |
723 | ␉␉keepMounted = 0;␊ |
724 | #if 0␊ |
725 | ␉printf("bootFile %s, devicePath %s, dontAsk %d\n", bootFile, devicePath, dontAsk);␊ |
726 | #endif␊ |
727 | ␉if (checkDevicePath(devicePath) < 0)␊ |
728 | ␉␉return -1;␊ |
729 | ␉if (checkDevicePath2(devicePath) >= 0) {␊ |
730 | ␉␉if (isMediaWhole && !isMediaLeaf) {␊ |
731 | ␉␉␉fprintf(stderr, "%s is a whole disk\n", devicePath);␊ |
732 | ␉␉␉return -1;␊ |
733 | ␉␉}␊ |
734 | ␉␉switch (daVolumeKind) {␊ |
735 | ␉␉␉case _undetected:␊ |
736 | ␉␉␉case _exfat:␊ |
737 | ␉␉␉case _hfs:␊ |
738 | ␉␉␉case _msdos:␊ |
739 | ␉␉␉␉break;␊ |
740 | ␉␉␉default:␊ |
741 | ␉␉␉␉unsupported();␊ |
742 | ␉␉␉␉return -1;␊ |
743 | ␉␉}␊ |
744 | ␉␉if (isVolumeMounted && keepMounted)␊ |
745 | ␉␉␉isVolumeMounted = 0;␊ |
746 | ␉␉if (isVolumeMounted && umount(devicePath, forceUmount) < 0) {␊ |
747 | ␉␉␉if (forceUmount)␊ |
748 | ␉␉␉␉fprintf(stderr, "Unable to force umount %s, please try to umount it manually or reboot\n", devicePath);␊ |
749 | ␉␉␉else␊ |
750 | ␉␉␉␉fprintf(stderr, "Unable to umount %s, please 'diskutil umount' manually before running this program,\nor pass -u option to force umount it\n", devicePath);␊ |
751 | ␉␉␉return -1;␊ |
752 | ␉␉}␊ |
753 | ␉}␊ |
754 | ␉/*␊ |
755 | ␉ * Note:␊ |
756 | ␉ * Reading a non-multiple of 512 does not work on /dev/rdisk␊ |
757 | ␉ */␊ |
758 | ␉if (loadChunk(devicePath, daVolumeKind == _hfs ? 1024 : 0, 512, &bpbBlob) < 0)␊ |
759 | ␉␉goto remount_and_error;␊ |
760 | ␉if (checkSupportedVolume(&daVolumeKind, &bpbBlob, devicePath) < 0)␊ |
761 | ␉␉goto cleanup_and_error;␊ |
762 | ␉if (!bootFile) {␊ |
763 | ␉␉switch (daVolumeKind) {␊ |
764 | ␉␉␉case _exfat:␊ |
765 | ␉␉␉␉bootFile = &defaultBootFile_exfat[0];␊ |
766 | ␉␉␉␉break;␊ |
767 | ␉␉␉case _hfs:␊ |
768 | ␉␉␉␉bootFile = &defaultBootFile_hfs[0];␊ |
769 | ␉␉␉␉break;␊ |
770 | ␉␉␉case _msdos:␊ |
771 | ␉␉␉␉bootFile = &defaultBootFile_fat32[0];␊ |
772 | ␉␉␉␉break;␊ |
773 | ␉␉␉default:␊ |
774 | ␉␉␉␉assert(0);␊ |
775 | ␉␉␉␉break;␊ |
776 | ␉␉}␊ |
777 | ␉␉printf("Using %s as default boot template\n", bootFile);␊ |
778 | ␉}␊ |
779 | ␉if (loadChunk(bootFile, 0, 0, &bootBlob) < 0)␊ |
780 | ␉␉goto cleanup_and_error;␊ |
781 | ␉switch (daVolumeKind) {␊ |
782 | ␉␉case _exfat:␊ |
783 | ␉␉␉if (calcSum(&bootBlob, &bpbBlob, &outputBlob, devicePath) < 0)␊ |
784 | ␉␉␉␉goto cleanup_and_error;␊ |
785 | ␉␉␉break;␊ |
786 | ␉␉case _hfs:␊ |
787 | ␉␉␉free_buffer(&bpbBlob);␊ |
788 | ␉␉␉if (bootBlob._s != 1024U) {␊ |
789 | ␉␉␉␉fprintf(stderr, "Boot Code size must be 1024 bytes\n");␊ |
790 | ␉␉␉␉goto cleanup_and_error;␊ |
791 | ␉␉␉}␊ |
792 | ␉␉␉break;␊ |
793 | ␉␉case _msdos:␊ |
794 | ␉␉␉if (fat32Layout(&bootBlob, &bpbBlob, &outputBlob) < 0)␊ |
795 | ␉␉␉␉goto cleanup_and_error;␊ |
796 | ␉␉␉break;␊ |
797 | ␉␉default:␊ |
798 | ␉␉␉assert(0);␊ |
799 | ␉␉␉break;␊ |
800 | ␉}␊ |
801 | ␉if (!dontAsk) {␊ |
802 | ␉␉printf("About to write new boot record on %s, Are You Sure (Y/N)?", devicePath);␊ |
803 | ␉␉ch = 0;␊ |
804 | ␉␉while (ch != 'Y' && ch != 'N')␊ |
805 | ␉␉␉ch = getchar();␊ |
806 | ␉␉if (ch != 'Y') {␊ |
807 | ␉␉␉printf("Aborted due to user request\n");␊ |
808 | ␉␉␉goto cleanup_and_exit;␊ |
809 | ␉␉}␊ |
810 | ␉}␊ |
811 | ␉switch (daVolumeKind) {␊ |
812 | ␉␉case _exfat:␊ |
813 | ␉␉␉writeVBR(devicePath, &outputBlob, 2, 12U * 512U, "exFAT");␊ |
814 | ␉␉␉break;␊ |
815 | ␉␉case _hfs:␊ |
816 | ␉␉␉writeVBR(devicePath, &bootBlob, 1, 1024U, "HFS+");␊ |
817 | ␉␉␉break;␊ |
818 | ␉␉case _msdos:␊ |
819 | ␉␉␉writeVBR(devicePath, &outputBlob, 1, 512U, "FAT32");␊ |
820 | ␉␉␉break;␊ |
821 | ␉␉default:␊ |
822 | ␉␉␉assert(0);␊ |
823 | ␉␉␉break;␊ |
824 | ␉}␊ |
825 | ␊ |
826 | cleanup_and_exit:␊ |
827 | ␉cleanup();␊ |
828 | ␉if (isVolumeMounted)␊ |
829 | ␉␉mount(devicePath);␊ |
830 | ␉return 0;␊ |
831 | ␊ |
832 | cleanup_and_error:␊ |
833 | ␉cleanup();␊ |
834 | remount_and_error:␊ |
835 | ␉if (isVolumeMounted)␊ |
836 | ␉␉mount(devicePath);␊ |
837 | ␉return -1;␊ |
838 | ␊ |
839 | usage_and_error:␊ |
840 | ␉usage(argv[0]);␊ |
841 | ␉return -1;␊ |
842 | }␊ |
843 | |