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