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