]> git.saurik.com Git - hfs.git/blob - hfs_util/hfsutil_main.c
hfs-165.tar.gz
[hfs.git] / hfs_util / hfsutil_main.c
1 /*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.2 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 Copyright (c) 1987-99 Apple Computer, Inc.
24 All Rights Reserved.
25
26 About hfs.util.m:
27 Contains code to implement hfs utility used by the WorkSpace to mount HFS.
28
29 Change History:
30 5-Jan-1999 Don Brady Write hfs.label names in UTF-8.
31 10-Dec-1998 Pat Dirks Changed to try built-in hfs filesystem first.
32 3-Sep-1998 Don Brady Disable the daylight savings time stuff.
33 28-Aug-1998 chw Fixed parse args and verify args to indicate that the
34 flags (fixed or removable) are required in the probe case.
35 22-Jun-1998 Pat Dirks Changed HFSToUFSStr table to switch ":" and "/".
36 13-Jan-1998 jwc first cut (derived from old NextStep macfs.util code and cdrom.util code).
37 */
38
39
40 /* ************************************** I N C L U D E S ***************************************** */
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/sysctl.h>
46 #include <sys/resource.h>
47 #include <sys/vmmeter.h>
48 #include <sys/mount.h>
49 #include <sys/wait.h>
50 #include <sys/param.h>
51 #include <sys/ucred.h>
52 #include <sys/disk.h>
53 #include <sys/loadable_fs.h>
54 #include <sys/attr.h>
55 #include <hfs/hfs_format.h>
56 #include <hfs/hfs_mount.h>
57
58 #include <ctype.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <pwd.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <syslog.h>
67
68 /*
69 * CommonCrypto provides a more stable API than OpenSSL guarantees;
70 * the #define causes it to use the same API for MD5 and SHA1, so the rest of
71 * the code need not change.
72 */
73 #define COMMON_DIGEST_FOR_OPENSSL
74 #include <CommonCrypto/CommonDigest.h>
75
76 #include <libkern/OSByteOrder.h>
77
78 #include <CoreFoundation/CFString.h>
79
80 #include <System/uuid/uuid.h>
81 #include <System/uuid/namespace.h>
82
83 #define READ_DEFAULT_ENCODING 1
84
85 #ifndef FSUC_ADOPT
86 #define FSUC_ADOPT 'a'
87 #endif
88
89 #ifndef FSUC_DISOWN
90 #define FSUC_DISOWN 'd'
91 #endif
92
93 #ifndef FSUC_GETUUID
94 #define FSUC_GETUUID 'k'
95 #endif
96
97 #ifndef FSUC_SETUUID
98 #define FSUC_SETUUID 's'
99 #endif
100
101 #ifndef FSUC_MKJNL
102 #define FSUC_MKJNL 'J'
103 #endif
104
105 #ifndef FSUC_UNJNL
106 #define FSUC_UNJNL 'U'
107 #endif
108
109 #ifndef FSUC_UNJNL_RAW
110 #define FSUC_UNJNL_RAW 'N'
111 #endif
112
113 #ifndef FSUC_JNLINFO
114 #define FSUC_JNLINFO 'I'
115 #endif
116
117
118 /* **************************************** L O C A L S ******************************************* */
119
120 #define HFS_BLOCK_SIZE 512
121
122 char gHFS_FS_NAME[] = "hfs";
123 char gHFS_FS_NAME_NAME[] = "HFS";
124
125 char gNewlineString[] = "\n";
126
127 char gMountCommand[] = "/sbin/mount";
128
129 char gUnmountCommand[] = "/sbin/umount";
130
131 char gReadOnlyOption[] = "-r";
132 char gReadWriteOption[] = "-w";
133
134 char gSuidOption[] = "suid";
135 char gNoSuidOption[] = "nosuid";
136
137 char gDevOption[] = "dev";
138 char gNoDevOption[] = "nodev";
139
140 char gUsePermissionsOption[] = "perm";
141 char gIgnorePermissionsOption[] = "noperm";
142
143 boolean_t gIsEjectable = 0;
144
145 int gJournalSize = 0;
146
147 #define AUTO_ADOPT_FIXED 1
148 #define AUTO_ENTER_FIXED 0
149
150
151 struct FinderAttrBuf {
152 unsigned long info_length;
153 unsigned long finderinfo[8];
154 };
155
156
157 #define VOLUMEUUIDVALUESIZE 2
158 typedef union VolumeUUID {
159 unsigned long value[VOLUMEUUIDVALUESIZE];
160 struct {
161 unsigned long high;
162 unsigned long low;
163 } v;
164 } VolumeUUID;
165
166 #define VOLUMEUUIDLENGTH 16
167 typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1];
168
169 #define VOLUME_RECORDED 0x80000000
170 #define VOLUME_USEPERMISSIONS 0x00000001
171 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
172
173 typedef void *VolumeStatusDBHandle;
174
175 void GenerateVolumeUUID(VolumeUUID *newVolumeID);
176 void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID);
177 void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString);
178 int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr);
179 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus);
180 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus);
181 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID);
182 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle);
183
184 /* ************************************ P R O T O T Y P E S *************************************** */
185 static void DoDisplayUsage( const char * argv[] );
186 static int DoMount( char * theDeviceNamePtr, const char * theMountPointPtr, boolean_t isLocked, boolean_t isSetuid, boolean_t isDev );
187 static int DoProbe( char * theDeviceNamePtr );
188 static int DoUnmount( const char * theMountPointPtr );
189 static int DoGetUUIDKey( const char * theDeviceNamePtr );
190 static int DoChangeUUIDKey( const char * theDeviceNamePtr );
191 static int DoAdopt( const char * theDeviceNamePtr );
192 static int DoDisown( const char * theDeviceNamePtr );
193
194 extern int DoMakeJournaled( const char * volNamePtr, int journalSize ); // XXXdbg
195 extern int DoUnJournal( const char * volNamePtr ); // XXXdbg
196 extern int DoGetJournalInfo( const char * volNamePtr );
197 extern int RawDisableJournaling( const char *devname );
198
199 static int ParseArgs( int argc, const char * argv[], const char ** actionPtr, const char ** mountPointPtr, boolean_t * isEjectablePtr, boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr );
200
201 static int GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr);
202 static int ReadHeaderBlock(int fd, void *bufptr, off_t *startOffset, VolumeUUID **finderInfoUUIDPtr);
203 static int GetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr);
204 static int GetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr);
205 static int GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate);
206 static int SetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr);
207 static int SetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr);
208 static int SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr);
209 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr);
210 static int GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o);
211 static int GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize,
212 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
213 u_int32_t *nodeSize, u_int32_t *firstLeafNode);
214 static int GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, HFSPlusVolumeHeader *volHdrPtr,
215 HFSPlusExtentDescriptor **catalogExtents, u_int32_t *catalogExtCount);
216 static int LogicalToPhysical(off_t logicalOffset, ssize_t length, u_int32_t blockSize,
217 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
218 off_t *physicalOffset, ssize_t *availableBytes);
219 static int ReadFile(int fd, void *buffer, off_t offset, ssize_t length,
220 off_t volOffset, u_int32_t blockSize,
221 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList);
222 static ssize_t readAt( int fd, void * buf, off_t offset, ssize_t length );
223 static ssize_t writeAt( int fd, void * buf, off_t offset, ssize_t length );
224
225 static int GetEncodingBias(void);
226
227
228 CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, UInt8 *buffer, CFIndex maxBufLen);
229
230 static void uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen);
231
232 /*
233 * The fuction CFStringGetSystemEncoding does not work correctly in
234 * our context (autodiskmount deamon). We include a local copy here
235 * so that we can derive the default encoding. Radar 2516316.
236 */
237 #if READ_DEFAULT_ENCODING
238 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
239
240 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
241 struct passwd *passwdp;
242
243 if ((passwdp = getpwuid(0))) { // root account
244 char buffer[MAXPATHLEN + 1];
245 int fd;
246
247 strcpy(buffer, passwdp->pw_dir);
248 strcat(buffer, __kCFUserEncodingFileName);
249
250 if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
251 size_t readSize;
252
253 readSize = read(fd, buffer, MAXPATHLEN);
254 buffer[(readSize < 0 ? 0 : readSize)] = '\0';
255 close(fd);
256 return strtol(buffer, NULL, 0);
257 }
258 }
259 return 0; // Fallback to smRoman
260 }
261 #endif
262
263
264 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
265
266 struct hfs_mnt_encoding {
267 char encoding_name[MXENCDNAMELEN]; /* encoding type name */
268 CFStringEncoding encoding_id; /* encoding type number */
269 };
270
271 static struct hfs_mnt_encoding hfs_mnt_encodinglist[] = {
272 { "Arabic", 4 },
273 { "Armenian", 24 },
274 { "Bengali", 13 },
275 { "Burmese", 19 },
276 { "Celtic", 39 },
277 { "CentralEurRoman", 29 },
278 { "ChineseSimp", 25 },
279 { "ChineseTrad", 2 },
280 { "Croatian", 36 },
281 { "Cyrillic", 7 },
282 { "Devanagari", 9 },
283 { "Ethiopic", 28 },
284 { "Farsi", 140 },
285 { "Gaelic", 40 },
286 { "Georgian", 23 },
287 { "Greek", 6 },
288 { "Gujarati", 11 },
289 { "Gurmukhi", 10 },
290 { "Hebrew", 5 },
291 { "Icelandic", 37 },
292 { "Japanese", 1 },
293 { "Kannada", 16 },
294 { "Khmer", 20 },
295 { "Korean", 3 },
296 { "Laotian", 22 },
297 { "Malayalam", 17 },
298 { "Mongolian", 27 },
299 { "Oriya", 12 },
300 { "Roman", 0 }, /* default */
301 { "Romanian", 38 },
302 { "Sinhalese", 18 },
303 { "Tamil", 14 },
304 { "Telugu", 15 },
305 { "Thai", 21 },
306 { "Tibetan", 26 },
307 { "Turkish", 35 },
308 { "Ukrainian", 152 },
309 { "Vietnamese", 30 },
310 };
311
312 #define KEXT_LOAD_COMMAND "/sbin/kextload"
313 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
314
315 static int load_encoding(CFStringEncoding encoding)
316 {
317 int i;
318 int numEncodings;
319 int pid;
320 char *encodingName;
321 struct stat sb;
322 union wait status;
323 char kmodfile[MAXPATHLEN];
324
325 /* Find the encoding that matches the one passed in */
326 numEncodings = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
327 encodingName = NULL;
328 for (i=0; i<numEncodings; ++i)
329 {
330 if (hfs_mnt_encodinglist[i].encoding_id == encoding)
331 {
332 encodingName = hfs_mnt_encodinglist[i].encoding_name;
333 break;
334 }
335 }
336
337 if (encodingName == NULL)
338 {
339 /* Couldn't figure out which encoding KEXT to load */
340 syslog(LOG_ERR, "Couldn't find name for encoding #%d", encoding);
341 return FSUR_LOADERR;
342 }
343
344 sprintf(kmodfile, "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH, encodingName);
345 if (stat(kmodfile, &sb) == -1)
346 {
347 /* We recognized the encoding, but couldn't find the KEXT */
348 syslog(LOG_ERR, "Couldn't stat HFS_Mac%s.kext: %s", encodingName, strerror(errno));
349 return FSUR_LOADERR;
350 }
351
352 pid = fork();
353 if (pid == 0)
354 {
355 (void) execl(KEXT_LOAD_COMMAND, KEXT_LOAD_COMMAND, "-q", kmodfile, NULL);
356
357 exit(1); /* We can only get here if the exec failed */
358 }
359 else if (pid != -1)
360 {
361 if ((waitpid(pid, (int *)&status, 0) == pid) && WIFEXITED(status))
362 {
363 if (WEXITSTATUS(status) != 0)
364 {
365 /* kextload returned an error. Too bad its output doesn't get logged. */
366 syslog(LOG_ERR, "Couldn't load HFS_Mac%s.kext", encodingName);
367 return FSUR_LOADERR;
368 }
369 }
370 }
371
372 return FSUR_IO_SUCCESS;
373 }
374
375
376 /* ******************************************** main ************************************************
377 Purpose -
378 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
379 for detail info on input arguments.
380 Input -
381 argc - the number of arguments in argv.
382 argv - array of arguments.
383 Output -
384 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
385 *************************************************************************************************** */
386
387 int main (int argc, const char *argv[])
388 {
389 const char * actionPtr = NULL;
390 char rawDeviceName[MAXPATHLEN];
391 char blockDeviceName[MAXPATHLEN];
392 const char * mountPointPtr = NULL;
393 int result = FSUR_IO_SUCCESS;
394 boolean_t isLocked = 0; /* reasonable assumptions */
395 boolean_t isSetuid = 0; /* reasonable assumptions */
396 boolean_t isDev = 0; /* reasonable assumptions */
397
398 openlog("hfs.util", LOG_PID, LOG_DAEMON);
399
400 /* Verify our arguments */
401 if ( (result = ParseArgs( argc, argv, & actionPtr, & mountPointPtr, & gIsEjectable, & isLocked, &isSetuid, &isDev )) != 0 ) {
402 goto AllDone;
403 }
404
405 /*
406 -- Build our device name (full path), should end up with something like:
407 -- "/dev/disk0s2"
408 */
409
410 sprintf(rawDeviceName, "/dev/r%s", argv[2]);
411 sprintf(blockDeviceName, "/dev/%s", argv[2]);
412
413 /* call the appropriate routine to handle the given action argument after becoming root */
414
415 switch( * actionPtr ) {
416 case FSUC_PROBE:
417 result = DoProbe(rawDeviceName);
418 break;
419
420 case FSUC_MOUNT:
421 case FSUC_MOUNT_FORCE:
422 result = DoMount(blockDeviceName, mountPointPtr, isLocked, isSetuid, isDev);
423 break;
424
425 case FSUC_UNMOUNT:
426 result = DoUnmount( mountPointPtr );
427 break;
428 case FSUC_GETUUID:
429 result = DoGetUUIDKey( blockDeviceName );
430 break;
431
432 case FSUC_SETUUID:
433 result = DoChangeUUIDKey( blockDeviceName );
434 break;
435 case FSUC_ADOPT:
436 result = DoAdopt( blockDeviceName );
437 break;
438
439 case FSUC_DISOWN:
440 result = DoDisown( blockDeviceName );
441 break;
442
443 case FSUC_MKJNL:
444 if (gJournalSize) {
445 result = DoMakeJournaled( argv[3], gJournalSize );
446 } else {
447 result = DoMakeJournaled( argv[2], gJournalSize );
448 }
449 break;
450
451 case FSUC_UNJNL:
452 result = DoUnJournal( argv[2] );
453 break;
454
455 case FSUC_UNJNL_RAW:
456 result = RawDisableJournaling( argv[2] );
457 break;
458
459 case FSUC_JNLINFO:
460 result = DoGetJournalInfo( argv[2] );
461 break;
462
463 default:
464 /* should never get here since ParseArgs should handle this situation */
465 DoDisplayUsage( argv );
466 result = FSUR_INVAL;
467 break;
468 }
469
470 AllDone:
471
472 exit(result);
473
474 return result; /*...and make main fit the ANSI spec. */
475 }
476
477
478 /* ***************************** DoMount ********************************
479 Purpose -
480 This routine will fire off a system command to mount the given device at the given mountpoint.
481 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
482 Input -
483 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
484 mountPointPtr - pointer to the mount point.
485 isLocked - a flag
486 Output -
487 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
488 *********************************************************************** */
489 static int
490 DoMount(char *deviceNamePtr, const char *mountPointPtr, boolean_t isLocked, boolean_t isSetuid, boolean_t isDev)
491 {
492 int pid;
493 char *isLockedstr;
494 char *isSetuidstr;
495 char *isDevstr;
496 char *permissionsOption;
497 int result = FSUR_IO_FAIL;
498 union wait status;
499 char encodeopt[16] = "";
500 CFStringEncoding encoding;
501 VolumeUUID targetVolumeUUID;
502 VolumeStatusDBHandle vsdbhandle = NULL;
503 unsigned long targetVolumeStatus;
504
505 if (mountPointPtr == NULL || *mountPointPtr == '\0')
506 return (FSUR_IO_FAIL);
507
508 /* get the volume UUID to check if permissions should be used: */
509 targetVolumeStatus = 0;
510 if (((result = GetVolumeUUID(deviceNamePtr, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) ||
511 (targetVolumeUUID.v.high ==0) ||
512 (targetVolumeUUID.v.low == 0)) {
513 #if TRACE_HFS_UTIL
514 fprintf(stderr, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result);
515 #endif
516 #if AUTO_ADOPT_FIXED
517 if (gIsEjectable == 0) {
518 result = DoAdopt( deviceNamePtr );
519 #if TRACE_HFS_UTIL
520 fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
521 #endif
522 targetVolumeStatus = VOLUME_USEPERMISSIONS;
523 } else {
524 #if TRACE_HFS_UTIL
525 fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
526 #endif
527 targetVolumeStatus = 0;
528 }
529 #endif
530 } else {
531 /* We've got a real volume UUID! */
532 #if TRACE_HFS_UTIL
533 fprintf(stderr, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID.v.high, targetVolumeUUID.v.low);
534 #endif
535 if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) {
536 /* Can't even get access to the volume info db; assume permissions are OK. */
537 #if TRACE_HFS_UTIL
538 fprintf(stderr, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result);
539 #endif
540 targetVolumeStatus = VOLUME_USEPERMISSIONS;
541 } else {
542 #if TRACE_HFS_UTIL
543 fprintf(stderr, "hfs.util: DoMount: Looking up volume status...\n");
544 #endif
545 if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
546 #if TRACE_HFS_UTIL
547 fprintf(stderr, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result);
548 #endif
549 #if AUTO_ENTER_FIXED
550 if (gIsEjectable == 0) {
551 result = DoAdopt( deviceNamePtr );
552 #if TRACE_HFS_UTIL
553 fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
554 #endif
555 targetVolumeStatus = VOLUME_USEPERMISSIONS;
556 } else {
557 #if TRACE_HFS_UTIL
558 fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
559 #endif
560 targetVolumeStatus = 0;
561 }
562 #else
563 targetVolumeStatus = 0;
564 #endif
565 }
566 (void)CloseVolumeStatusDB(vsdbhandle);
567 vsdbhandle = NULL;
568 }
569 }
570
571 pid = fork();
572 if (pid == 0) {
573 isLockedstr = isLocked ? gReadOnlyOption : gReadWriteOption;
574 isSetuidstr = isSetuid ? gSuidOption : gNoSuidOption;
575 isDevstr = isDev ? gDevOption : gNoDevOption;
576
577 permissionsOption =
578 (targetVolumeStatus & VOLUME_USEPERMISSIONS) ? gUsePermissionsOption : gIgnorePermissionsOption;
579
580 /* get default encoding value (for hfs volumes) */
581 #if READ_DEFAULT_ENCODING
582 encoding = __CFStringGetDefaultEncodingForHFSUtil();
583 #else
584 encoding = CFStringGetSystemEncoding();
585 #endif
586 sprintf(encodeopt, "-e=%d", (int)encoding);
587 #if TRACE_HFS_UTIL
588 fprintf(stderr, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
589 gMountCommand, isLockedstr, encodeopt, permissionsOption, gHFS_FS_NAME, deviceNamePtr, mountPointPtr);
590 #endif
591 (void) execl(gMountCommand, gMountCommand, isLockedstr, "-o", isSetuidstr, "-o", isDevstr,
592 "-o", encodeopt, "-o", permissionsOption,
593 "-o", "-u=unknown,-g=unknown,-m=0777",
594 "-t", gHFS_FS_NAME, deviceNamePtr, mountPointPtr, NULL);
595
596
597 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
598 return (FSUR_IO_FAIL);
599 }
600
601 if (pid == -1)
602 return (FSUR_IO_FAIL);
603
604 /* Success! */
605 if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
606 result = status.w_retcode;
607 else
608 result = -1;
609
610 return (result == 0) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
611 }
612
613
614 /* ****************************************** DoUnmount *********************************************
615 Purpose -
616 This routine will fire off a system command to unmount the given device.
617 Input -
618 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
619 Output -
620 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
621 *************************************************************************************************** */
622 static int
623 DoUnmount(const char * theMountPointPtr)
624 {
625 int pid;
626 union wait status;
627 int result;
628
629 if (theMountPointPtr == NULL || *theMountPointPtr == '\0') return (FSUR_IO_FAIL);
630
631 pid = fork();
632 if (pid == 0) {
633 #if TRACE_HFS_UTIL
634 fprintf(stderr, "hfs.util: %s %s ...\n", gUnmountCommand, theMountPointPtr);
635 #endif
636 (void) execl(gUnmountCommand, gUnmountCommand, theMountPointPtr, NULL);
637
638 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
639 return (FSUR_IO_FAIL);
640 }
641
642 if (pid == -1)
643 return (FSUR_IO_FAIL);
644
645 /* Success! */
646 if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
647 result = status.w_retcode;
648 else
649 result = -1;
650
651 return (result == 0 ? FSUR_IO_SUCCESS : FSUR_IO_FAIL);
652
653 } /* DoUnmount */
654
655
656 /* ******************************************* DoProbe **********************************************
657 Purpose -
658 This routine will open the given raw device and check to make sure there is media that looks
659 like an HFS.
660 Input -
661 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
662 Output -
663 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
664 *************************************************************************************************** */
665 static int
666 DoProbe(char *deviceNamePtr)
667 {
668 int result = FSUR_UNRECOGNIZED;
669 int fd = 0;
670 char * bufPtr;
671 HFSMasterDirectoryBlock * mdbPtr;
672 HFSPlusVolumeHeader * volHdrPtr;
673 u_char volnameUTF8[NAME_MAX+1];
674
675 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
676 if ( ! bufPtr ) {
677 result = FSUR_UNRECOGNIZED;
678 goto Return;
679 }
680
681 mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
682 volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
683
684 fd = open( deviceNamePtr, O_RDONLY, 0 );
685 if( fd <= 0 ) {
686 result = FSUR_IO_FAIL;
687 goto Return;
688 }
689
690 /*
691 * Read the HFS Master Directory Block from sector 2
692 */
693 result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
694 if (FSUR_IO_FAIL == result)
695 goto Return;
696
697 /* get classic HFS volume name (from MDB) */
698 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
699 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) {
700 Boolean cfOK;
701 CFStringRef cfstr;
702 CFStringEncoding encoding;
703
704 /* Some poorly mastered HFS CDs have an empty MDB name field! */
705 if (mdbPtr->drVN[0] == '\0') {
706 strcpy((char *)&mdbPtr->drVN[1], gHFS_FS_NAME_NAME);
707 mdbPtr->drVN[0] = strlen(gHFS_FS_NAME_NAME);
708 }
709
710 /* Check for an encoding hint in the Finder Info (field 4). */
711 encoding = GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr->drFndrInfo[4]));
712 if (encoding == kCFStringEncodingInvalidId) {
713 /* Next try the encoding bias in the kernel. */
714 encoding = GetEncodingBias();
715 if (encoding == 0 || encoding == kCFStringEncodingInvalidId)
716 encoding = __CFStringGetDefaultEncodingForHFSUtil();
717 }
718
719 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
720 mdbPtr->drVN, encoding);
721 cfOK = _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
722 CFRelease(cfstr);
723
724 if (!cfOK && encoding != kCFStringEncodingMacRoman) {
725
726 /* default to MacRoman on conversion errors */
727 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
728 mdbPtr->drVN, kCFStringEncodingMacRoman);
729 _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
730 CFRelease(cfstr);
731 encoding = kCFStringEncodingMacRoman;
732 }
733
734 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
735 if (encoding != kCFStringEncodingMacRoman) {
736 if (load_encoding(encoding) != FSUR_IO_SUCCESS) {
737 encoding = kCFStringEncodingMacRoman;
738 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, encoding);
739 _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
740 CFRelease(cfstr);
741 }
742 }
743
744 /* get HFS Plus volume name (from Catalog) */
745 } else if ((OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord) ||
746 (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) ||
747 (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
748 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord)) {
749 off_t startOffset;
750
751 if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSSigWord) {
752 /* embedded volume, first find offset */
753 result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset);
754 if ( result != FSUR_IO_SUCCESS )
755 goto Return;
756 } else {
757 startOffset = 0;
758 }
759
760 result = GetNameFromHFSPlusVolumeStartingAt(fd, startOffset,
761 volnameUTF8);
762 } else {
763 result = FSUR_UNRECOGNIZED;
764 }
765
766 if (FSUR_IO_SUCCESS == result) {
767 unsigned char *s;
768
769 /* Change slashes to colons in the volume name */
770 for (s=volnameUTF8; *s; ++s) {
771 if (*s == '/')
772 *s = ':';
773 }
774
775 /* Print the volume name to standard output */
776 write(1, volnameUTF8, strlen((char *)volnameUTF8));
777 result = FSUR_RECOGNIZED;
778 }
779
780 Return:
781
782 if ( bufPtr )
783 free( bufPtr );
784
785 if (fd > 0)
786 close(fd);
787
788 return result;
789
790 } /* DoProbe */
791
792 /*
793 * Create a version 3 UUID from a unique "name" in the given "name space".
794 * Version 3 UUID are derived using "name" via MD5 checksum.
795 *
796 * Parameters:
797 * result_uuid - resulting UUID.
798 * namespace - namespace in which given name exists and UUID should be created.
799 * name - unique string used to create version 3 UUID.
800 * namelen - length of the name string.
801 */
802 static void
803 uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen)
804 {
805 MD5_CTX c;
806
807 MD5_Init(&c);
808 MD5_Update(&c, namespace, sizeof(uuid_t));
809 MD5_Update(&c, name, namelen);
810 MD5_Final(result_uuid, &c);
811
812 result_uuid[6] = (result_uuid[6] & 0x0F) | 0x30;
813 result_uuid[8] = (result_uuid[8] & 0x3F) | 0x80;
814 }
815
816
817 /* **************************************** DoGetUUIDKey *******************************************
818 Purpose -
819 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
820 Input -
821 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
822 Output -
823 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
824 *************************************************************************************************** */
825 static int
826 DoGetUUIDKey( const char * theDeviceNamePtr ) {
827 int result;
828 VolumeUUID targetVolumeUUID;
829 uuid_t uuid;
830 char uuidLine[40];
831
832 unsigned char rawUUID[8];
833
834 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) goto Err_Exit;
835
836 ((uint32_t *)rawUUID)[0] = OSSwapHostToBigInt32(targetVolumeUUID.v.high);
837 ((uint32_t *)rawUUID)[1] = OSSwapHostToBigInt32(targetVolumeUUID.v.low);
838
839 uuid_create_md5_from_name(uuid, kFSUUIDNamespaceSHA1, rawUUID, sizeof(rawUUID));
840 uuid_unparse(uuid, uuidLine);
841 write(1, uuidLine, strlen(uuidLine));
842 result = FSUR_IO_SUCCESS;
843
844 Err_Exit:
845 return result;
846 }
847
848
849
850 /* *************************************** DoChangeUUIDKey ******************************************
851 Purpose -
852 This routine will change the UUID on the specified block device.
853 Input -
854 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
855 Output -
856 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
857 *************************************************************************************************** */
858 static int
859 DoChangeUUIDKey( const char * theDeviceNamePtr ) {
860 int result;
861 VolumeUUID newVolumeUUID;
862
863 GenerateVolumeUUID(&newVolumeUUID);
864 result = SetVolumeUUID(theDeviceNamePtr, &newVolumeUUID);
865
866 return result;
867 }
868
869
870
871 /* **************************************** DoAdopt *******************************************
872 Purpose -
873 This routine will add the UUID of the specified block device to the list of local volumes.
874 Input -
875 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
876 Output -
877 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
878 *************************************************************************************************** */
879 static int
880 DoAdopt( const char * theDeviceNamePtr ) {
881 int result, closeresult;
882 VolumeUUID targetVolumeUUID;
883 VolumeStatusDBHandle vsdbhandle = NULL;
884 unsigned long targetVolumeStatus;
885
886 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
887
888 if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
889 if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
890 targetVolumeStatus = 0;
891 }
892 targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
893 if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
894
895 result = FSUR_IO_SUCCESS;
896
897 Err_Exit:
898 if (vsdbhandle) {
899 closeresult = CloseVolumeStatusDB(vsdbhandle);
900 vsdbhandle = NULL;
901 if (result == FSUR_IO_SUCCESS) result = closeresult;
902 }
903
904 if ((result != 0) && (result != FSUR_IO_SUCCESS)) result = FSUR_IO_FAIL;
905
906 Err_Return:
907 #if TRACE_HFS_UTIL
908 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoAdopt: returning %d...\n", result);
909 #endif
910 return result;
911 }
912
913
914
915 /* **************************************** DoDisown *******************************************
916 Purpose -
917 This routine will change the status of the specified block device to ignore its permissions.
918 Input -
919 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
920 Output -
921 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
922 *************************************************************************************************** */
923 static int
924 DoDisown( const char * theDeviceNamePtr ) {
925 int result, closeresult;
926 VolumeUUID targetVolumeUUID;
927 VolumeStatusDBHandle vsdbhandle = NULL;
928 unsigned long targetVolumeStatus;
929
930 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
931
932 if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
933 if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
934 targetVolumeStatus = 0;
935 }
936 targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
937 if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
938
939 result = FSUR_IO_SUCCESS;
940
941 Err_Exit:
942 if (vsdbhandle) {
943 closeresult = CloseVolumeStatusDB(vsdbhandle);
944 vsdbhandle = NULL;
945 if (result == FSUR_IO_SUCCESS) result = closeresult;
946 }
947
948 if ((result != 0) && (result != FSUR_IO_SUCCESS)) {
949 #if TRACE_HFS_UTIL
950 if (result != 0) fprintf(stderr, "DoDisown: result = %d; changing to %d...\n", result, FSUR_IO_FAIL);
951 #endif
952 result = FSUR_IO_FAIL;
953 }
954
955 Err_Return:
956 #if TRACE_HFS_UTIL
957 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoDisown: returning %d...\n", result);
958 #endif
959 return result;
960 }
961
962
963 static int
964 get_multiplier(char c)
965 {
966 if (tolower(c) == 'k') {
967 return 1024;
968 } else if (tolower(c) == 'm') {
969 return 1024 * 1024;
970 } else if (tolower(c) == 'g') {
971 return 1024 * 1024 * 1024;
972 }
973
974 return 1;
975 }
976
977 /* **************************************** ParseArgs ********************************************
978 Purpose -
979 This routine will make sure the arguments passed in to us are cool.
980 Here is how this utility is used:
981
982 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
983 actionArg:
984 -p (Probe for mounting)
985 -P (Probe for initializing - not supported)
986 -m (Mount)
987 -r (Repair - not supported)
988 -u (Unmount)
989 -M (Force Mount)
990 -i (Initialize - not supported)
991
992 deviceArg:
993 disk0s2 (for example)
994
995 mountPointArg:
996 /foo/bar/ (required for Mount and Force Mount actions)
997
998 flagsArg:
999 (these are ignored for CDROMs)
1000 either "readonly" OR "writable"
1001 either "removable" OR "fixed"
1002 either "nosuid" or "suid"
1003 either "nodev" or "dev"
1004
1005 examples:
1006 hfs.util -p disk0s2 removable writable
1007 hfs.util -p disk0s2 removable readonly
1008 hfs.util -m disk0s2 /my/hfs
1009
1010 Input -
1011 argc - the number of arguments in argv.
1012 argv - array of arguments.
1013 Output -
1014 returns FSUR_INVAL if we find a bad argument else 0.
1015 *************************************************************************************************** */
1016 static int
1017 ParseArgs(int argc, const char *argv[], const char ** actionPtr,
1018 const char ** mountPointPtr, boolean_t * isEjectablePtr,
1019 boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr)
1020 {
1021 int result = FSUR_INVAL;
1022 int deviceLength, doLengthCheck = 1;
1023 int index;
1024 int mounting = 0;
1025
1026 /* Must have at least 3 arguments and the action argument must start with a '-' */
1027 if ( (argc < 3) || (argv[1][0] != '-') ) {
1028 DoDisplayUsage( argv );
1029 goto Return;
1030 }
1031
1032 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1033
1034 * actionPtr = & argv[1][1];
1035
1036 switch ( argv[1][1] ) {
1037 case FSUC_PROBE:
1038 /* action Probe and requires 5 arguments (need the flags) */
1039 if ( argc < 5 ) {
1040 DoDisplayUsage( argv );
1041 goto Return;
1042 } else {
1043 index = 3;
1044 }
1045 break;
1046
1047 case FSUC_UNMOUNT:
1048 /* Note: the device argument in argv[2] is checked further down but ignored. */
1049 * mountPointPtr = argv[3];
1050 index = 0; /* No isEjectable/isLocked flags for unmount. */
1051 break;
1052
1053 case FSUC_MOUNT:
1054 case FSUC_MOUNT_FORCE:
1055 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1056 if ( argc < 8 ) {
1057 DoDisplayUsage( argv );
1058 goto Return;
1059 } else {
1060 * mountPointPtr = argv[3];
1061 index = 4;
1062 mounting = 1;
1063 }
1064 break;
1065
1066 case FSUC_GETUUID:
1067 index = 0;
1068 break;
1069
1070 case FSUC_SETUUID:
1071 index = 0;
1072 break;
1073
1074 case FSUC_ADOPT:
1075 index = 0;
1076 break;
1077
1078 case FSUC_DISOWN:
1079 index = 0;
1080 break;
1081
1082 // XXXdbg
1083 case FSUC_MKJNL:
1084 index = 0;
1085 doLengthCheck = 0;
1086 if (isdigit(argv[2][0])) {
1087 char *ptr;
1088 gJournalSize = strtoul(argv[2], &ptr, 0);
1089 if (ptr) {
1090 gJournalSize *= get_multiplier(*ptr);
1091 }
1092 return 0;
1093 }
1094 break;
1095
1096 case FSUC_UNJNL:
1097 index = 0;
1098 doLengthCheck = 0;
1099 break;
1100
1101 case FSUC_UNJNL_RAW:
1102 index = 0;
1103 doLengthCheck = 0;
1104 break;
1105
1106 case FSUC_JNLINFO:
1107 index = 0;
1108 doLengthCheck = 0;
1109 break;
1110 // XXXdbg
1111
1112 default:
1113 DoDisplayUsage( argv );
1114 goto Return;
1115 break;
1116 }
1117
1118 /* Make sure device (argv[2]) is something reasonable */
1119 deviceLength = strlen( argv[2] );
1120 if ( doLengthCheck && (deviceLength < 3 || deviceLength > NAME_MAX) ) {
1121 DoDisplayUsage( argv );
1122 goto Return;
1123 }
1124
1125 if ( index ) {
1126 /* Flags: removable/fixed. */
1127 if ( 0 == strcmp(argv[index],"removable") ) {
1128 * isEjectablePtr = 1;
1129 } else if ( 0 == strcmp(argv[index],"fixed") ) {
1130 * isEjectablePtr = 0;
1131 } else {
1132 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index,argv[index]);
1133 }
1134
1135 /* Flags: readonly/writable. */
1136 if ( 0 == strcmp(argv[index+1],"readonly") ) {
1137 * isLockedPtr = 1;
1138 } else if ( 0 == strcmp(argv[index+1],"writable") ) {
1139 * isLockedPtr = 0;
1140 } else {
1141 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index,argv[index+1]);
1142 }
1143
1144 if (mounting) {
1145 /* Flags: suid/nosuid. */
1146 if ( 0 == strcmp(argv[index+2],"suid") ) {
1147 * isSetuidPtr = 1;
1148 } else if ( 0 == strcmp(argv[index+2],"nosuid") ) {
1149 * isSetuidPtr = 0;
1150 } else {
1151 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index,argv[index+2]);
1152 }
1153
1154 /* Flags: dev/nodev. */
1155 if ( 0 == strcmp(argv[index+3],"dev") ) {
1156 * isDevPtr = 1;
1157 } else if ( 0 == strcmp(argv[index+3],"nodev") ) {
1158 * isDevPtr = 0;
1159 } else {
1160 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index,argv[index+3]);
1161 }
1162 }
1163
1164
1165 }
1166
1167 result = 0;
1168
1169 Return:
1170 return result;
1171
1172 } /* ParseArgs */
1173
1174
1175 /* *************************************** DoDisplayUsage ********************************************
1176 Purpose -
1177 This routine will do a printf of the correct usage for this utility.
1178 Input -
1179 argv - array of arguments.
1180 Output -
1181 NA.
1182 *************************************************************************************************** */
1183 static void
1184 DoDisplayUsage(const char *argv[])
1185 {
1186 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0]);
1187 printf("action_arg:\n");
1188 printf(" -%c (Probe for mounting)\n", FSUC_PROBE);
1189 printf(" -%c (Mount)\n", FSUC_MOUNT);
1190 printf(" -%c (Unmount)\n", FSUC_UNMOUNT);
1191 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE);
1192 #ifdef HFS_UUID_SUPPORT
1193 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID);
1194 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID);
1195 #endif HFS_UUID_SUPPORT
1196 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT);
1197 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL);
1198 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL);
1199 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW);
1200 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO);
1201 printf("device_arg:\n");
1202 printf(" device we are acting upon (for example, 'disk0s2')\n");
1203 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL, FSUC_UNJNL);
1204 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1205 printf("mount_point_arg:\n");
1206 printf(" required for Mount and Force Mount \n");
1207 printf("Flags:\n");
1208 printf(" required for Mount, Force Mount and Probe\n");
1209 printf(" indicates removable or fixed (for example 'fixed')\n");
1210 printf(" indicates readonly or writable (for example 'readonly')\n");
1211 printf(" indicates suid or nosuid (for example 'suid')\n");
1212 printf(" indicates dev or nodev (for example 'dev')\n");
1213 printf("Examples:\n");
1214 printf(" %s -p disk0s2 fixed writable\n", argv[0]);
1215 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv[0]);
1216
1217 return;
1218
1219 } /* DoDisplayUsage */
1220
1221
1222 /*
1223 GetHFSMountPoint
1224
1225 Given a path to a device, determine if a volume is mounted on that
1226 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1227 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1228 no volume mounted on the device, set *pathPtr to NULL and return
1229 FSUR_IO_SUCCESS.
1230
1231 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1232 */
1233 static int
1234 GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr)
1235 {
1236 int result;
1237 int i, numMounts;
1238 struct statfs *buf;
1239
1240 /* Assume no mounted volume found */
1241 *pathPtr = NULL;
1242 result = FSUR_IO_SUCCESS;
1243
1244 numMounts = getmntinfo(&buf, MNT_NOWAIT);
1245 if (numMounts == 0)
1246 return FSUR_IO_FAIL;
1247
1248 for (i=0; i<numMounts; ++i) {
1249 if (!strcmp(deviceNamePtr, buf[i].f_mntfromname)) {
1250 /* Found a mounted volume; check the type */
1251 if (!strcmp(buf[i].f_fstypename, "hfs")) {
1252 *pathPtr = buf[i].f_mntonname;
1253 /* result = FSUR_IO_SUCCESS, above */
1254 } else {
1255 result = FSUR_UNRECOGNIZED;
1256 }
1257 break;
1258 }
1259 }
1260
1261 return result;
1262 }
1263
1264
1265 /*
1266 ReadHeaderBlock
1267
1268 Read the Master Directory Block or Volume Header Block from an HFS,
1269 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1270 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1271 Return a pointer to the volume UUID in the Finder Info.
1272
1273 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1274 */
1275 static int
1276 ReadHeaderBlock(int fd, void *bufPtr, off_t *startOffset, VolumeUUID **finderInfoUUIDPtr)
1277 {
1278 int result;
1279 HFSMasterDirectoryBlock * mdbPtr;
1280 HFSPlusVolumeHeader * volHdrPtr;
1281
1282 mdbPtr = bufPtr;
1283 volHdrPtr = bufPtr;
1284
1285 /*
1286 * Read the HFS Master Directory Block or Volume Header from sector 2
1287 */
1288 *startOffset = 0;
1289 result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1290 if (result != FSUR_IO_SUCCESS)
1291 goto Err_Exit;
1292
1293 /*
1294 * If this is a wrapped HFS Plus volume, read the Volume Header from
1295 * sector 2 of the embedded volume.
1296 */
1297 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
1298 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) {
1299 result = GetEmbeddedHFSPlusVol(mdbPtr, startOffset);
1300 if (result != FSUR_IO_SUCCESS)
1301 goto Err_Exit;
1302 result = readAt(fd, bufPtr, *startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1303 if (result != FSUR_IO_SUCCESS)
1304 goto Err_Exit;
1305 }
1306
1307 /*
1308 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1309 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1310 * UUID from the Finder Info.
1311 */
1312 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) {
1313 *finderInfoUUIDPtr = (VolumeUUID *)(&mdbPtr->drFndrInfo[6]);
1314 } else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord ||
1315 OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) {
1316 *finderInfoUUIDPtr = (VolumeUUID *)&volHdrPtr->finderInfo[24];
1317 } else {
1318 result = FSUR_UNRECOGNIZED;
1319 }
1320
1321 Err_Exit:
1322 return result;
1323 }
1324
1325
1326 /*
1327 GetVolumeUUIDRaw
1328
1329 Read the UUID from an unmounted volume, by doing direct access to the device.
1330 Assumes the caller has already determined that a volume is not mounted
1331 on the device.
1332
1333 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1334 */
1335 static int
1336 GetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr)
1337 {
1338 int fd = 0;
1339 char * bufPtr;
1340 off_t startOffset;
1341 VolumeUUID *finderInfoUUIDPtr;
1342 int result;
1343
1344 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1345 if ( ! bufPtr ) {
1346 result = FSUR_UNRECOGNIZED;
1347 goto Err_Exit;
1348 }
1349
1350 fd = open( deviceNamePtr, O_RDONLY, 0);
1351 if (fd <= 0) {
1352 #if TRACE_HFS_UTIL
1353 fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno);
1354 #endif
1355 result = FSUR_IO_FAIL;
1356 goto Err_Exit;
1357 }
1358
1359 /*
1360 * Get the pointer to the volume UUID in the Finder Info
1361 */
1362 result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1363 if (result != FSUR_IO_SUCCESS)
1364 goto Err_Exit;
1365
1366 /*
1367 * Copy the volume UUID out of the Finder Info
1368 */
1369 volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1370 volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1371
1372 Err_Exit:
1373 if (fd > 0) close(fd);
1374 if (bufPtr) free(bufPtr);
1375
1376 #if TRACE_HFS_UTIL
1377 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result);
1378 #endif
1379 return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1380 }
1381
1382
1383 /*
1384 SetVolumeUUIDRaw
1385
1386 Write a previously generated UUID to an unmounted volume, by doing direct
1387 access to the device. Assumes the caller has already determined that a
1388 volume is not mounted on the device.
1389
1390 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1391 */
1392 static int
1393 SetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr)
1394 {
1395 int fd = 0;
1396 char * bufPtr;
1397 off_t startOffset;
1398 VolumeUUID *finderInfoUUIDPtr;
1399 int result;
1400
1401 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1402 if ( ! bufPtr ) {
1403 result = FSUR_UNRECOGNIZED;
1404 goto Err_Exit;
1405 }
1406
1407 fd = open( deviceNamePtr, O_RDWR, 0);
1408 if (fd <= 0) {
1409 #if TRACE_HFS_UTIL
1410 fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno);
1411 #endif
1412 result = FSUR_IO_FAIL;
1413 goto Err_Exit;
1414 }
1415
1416 /*
1417 * Get the pointer to the volume UUID in the Finder Info
1418 */
1419 result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1420 if (result != FSUR_IO_SUCCESS)
1421 goto Err_Exit;
1422
1423 /*
1424 * Update the UUID in the Finder Info
1425 */
1426 finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1427 finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1428
1429 /*
1430 * Write the modified MDB or VHB back to disk
1431 */
1432 result = writeAt(fd, bufPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1433
1434 Err_Exit:
1435 if (fd > 0) close(fd);
1436 if (bufPtr) free(bufPtr);
1437
1438 #if TRACE_HFS_UTIL
1439 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result);
1440 #endif
1441 return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1442 }
1443
1444
1445 /*
1446 GetVolumeUUIDAttr
1447
1448 Read the UUID from a mounted volume, by calling getattrlist().
1449 Assumes the path is the mount point of an HFS volume.
1450
1451 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1452 */
1453 static int
1454 GetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1455 {
1456 struct attrlist alist;
1457 struct FinderAttrBuf volFinderInfo;
1458 VolumeUUID *finderInfoUUIDPtr;
1459 int result;
1460
1461 /* Set up the attrlist structure to get the volume's Finder Info */
1462 alist.bitmapcount = 5;
1463 alist.reserved = 0;
1464 alist.commonattr = ATTR_CMN_FNDRINFO;
1465 alist.volattr = ATTR_VOL_INFO;
1466 alist.dirattr = 0;
1467 alist.fileattr = 0;
1468 alist.forkattr = 0;
1469
1470 /* Get the Finder Info */
1471 result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1472 if (result) {
1473 result = FSUR_IO_FAIL;
1474 goto Err_Exit;
1475 }
1476
1477 /* Copy the UUID from the Finder Into to caller's buffer */
1478 finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1479 volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1480 volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1481 result = FSUR_IO_SUCCESS;
1482
1483 Err_Exit:
1484 return result;
1485 }
1486
1487
1488 /*
1489 SetVolumeUUIDAttr
1490
1491 Write a UUID to a mounted volume, by calling setattrlist().
1492 Assumes the path is the mount point of an HFS volume.
1493
1494 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1495 */
1496 static int
1497 SetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1498 {
1499 struct attrlist alist;
1500 struct FinderAttrBuf volFinderInfo;
1501 VolumeUUID *finderInfoUUIDPtr;
1502 int result;
1503
1504 /* Set up the attrlist structure to get the volume's Finder Info */
1505 alist.bitmapcount = 5;
1506 alist.reserved = 0;
1507 alist.commonattr = ATTR_CMN_FNDRINFO;
1508 alist.volattr = ATTR_VOL_INFO;
1509 alist.dirattr = 0;
1510 alist.fileattr = 0;
1511 alist.forkattr = 0;
1512
1513 /* Get the Finder Info */
1514 result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1515 if (result) {
1516 result = FSUR_IO_FAIL;
1517 goto Err_Exit;
1518 }
1519
1520 /* Update the UUID in the Finder Info */
1521 finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1522 finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1523 finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1524
1525 /* Write the Finder Info back to the volume */
1526 result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
1527 if (result) {
1528 result = FSUR_IO_FAIL;
1529 goto Err_Exit;
1530 }
1531
1532 result = FSUR_IO_SUCCESS;
1533
1534 Err_Exit:
1535 return result;
1536 }
1537
1538
1539 /*
1540 GetVolumeUUID
1541
1542 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1543 we were asked to generate one, then generate a new UUID and write it to the
1544 volume.
1545
1546 Determine whether an HFS volume is mounted on the given device. If so, we
1547 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1548 the filesystem. If there is no mounted volume, then do direct device access
1549 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1550
1551 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1552 */
1553
1554 static int
1555 GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate)
1556 {
1557 int result;
1558 char *path = NULL;
1559
1560 /*
1561 * Determine whether a volume is mounted on this device. If it is HFS, then
1562 * get the mount point's path. If it is non-HFS, then we can exit immediately
1563 * with FSUR_UNRECOGNIZED.
1564 */
1565 result = GetHFSMountPoint(deviceNamePtr, &path);
1566 if (result != FSUR_IO_SUCCESS)
1567 goto Err_Exit;
1568
1569 /*
1570 * Get any existing UUID.
1571 */
1572 if (path)
1573 result = GetVolumeUUIDAttr(path, volumeUUIDPtr);
1574 else
1575 result = GetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1576 if (result != FSUR_IO_SUCCESS)
1577 goto Err_Exit;
1578
1579 /*
1580 * If there was no valid UUID, and we were asked to generate one, then
1581 * generate it and write it back to disk.
1582 */
1583 if (generate && (volumeUUIDPtr->v.high == 0 || volumeUUIDPtr->v.low == 0)) {
1584 GenerateVolumeUUID(volumeUUIDPtr);
1585 if (path)
1586 result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1587 else
1588 result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1589 /* Fall through to Err_Exit */
1590 }
1591
1592 Err_Exit:
1593 return result;
1594 }
1595
1596
1597
1598 /*
1599 SetVolumeUUID
1600
1601 Write a UUID to an HFS, HFS Plus or HFSX volume.
1602
1603 Determine whether an HFS volume is mounted on the given device. If so, we
1604 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1605 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1606
1607 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1608 */
1609 static int
1610 SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) {
1611 int result;
1612 char *path = NULL;
1613
1614 /*
1615 * Determine whether a volume is mounted on this device. If it is HFS, then
1616 * get the mount point's path. If it is non-HFS, then we can exit immediately
1617 * with FSUR_UNRECOGNIZED.
1618 */
1619 result = GetHFSMountPoint(deviceNamePtr, &path);
1620 if (result != FSUR_IO_SUCCESS)
1621 goto Err_Exit;
1622
1623 /*
1624 * Update the UUID.
1625 */
1626 if (path)
1627 result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1628 else
1629 result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1630
1631 Err_Exit:
1632 return result;
1633 }
1634
1635
1636
1637 /*
1638 -- GetEmbeddedHFSPlusVol
1639 --
1640 -- In: hfsMasterDirectoryBlockPtr
1641 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1642 (that is, 2 blocks before the volume header)
1643 --
1644 */
1645
1646 static int
1647 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr)
1648 {
1649 int result = FSUR_IO_SUCCESS;
1650 u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount;
1651
1652 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drSigWord) != kHFSSigWord) {
1653 result = FSUR_UNRECOGNIZED;
1654 goto Return;
1655 }
1656
1657 allocationBlockSize = OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr->drAlBlkSiz);
1658 firstAllocationBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drAlBlSt);
1659
1660 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedSigWord) != kHFSPlusSigWord) {
1661 result = FSUR_UNRECOGNIZED;
1662 goto Return;
1663 }
1664
1665 startBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock);
1666 blockCount = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount);
1667
1668 if ( startOffsetPtr )
1669 *startOffsetPtr = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) +
1670 ((u_int64_t)firstAllocationBlock * (u_int64_t)HFS_BLOCK_SIZE);
1671
1672 Return:
1673 return result;
1674
1675 }
1676
1677
1678
1679 /*
1680 -- GetNameFromHFSPlusVolumeStartingAt
1681 --
1682 -- Caller's responsibility to allocate and release memory for the converted string.
1683 --
1684 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1685 */
1686
1687 static int
1688 GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o)
1689 {
1690 int result = FSUR_IO_SUCCESS;
1691 u_int32_t blockSize;
1692 char * bufPtr = NULL;
1693 HFSPlusVolumeHeader * volHdrPtr;
1694 BTNodeDescriptor * bTreeNodeDescriptorPtr;
1695 u_int32_t catalogNodeSize;
1696 u_int32_t leafNode;
1697 u_int32_t catalogExtCount;
1698 HFSPlusExtentDescriptor *catalogExtents = NULL;
1699
1700 volHdrPtr = (HFSPlusVolumeHeader *)malloc(HFS_BLOCK_SIZE);
1701 if ( ! volHdrPtr ) {
1702 result = FSUR_IO_FAIL;
1703 goto Return;
1704 }
1705
1706 /*
1707 * Read the Volume Header
1708 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1709 */
1710 result = readAt( fd, volHdrPtr, hfsPlusVolumeOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE );
1711 if (result == FSUR_IO_FAIL) {
1712 #if TRACE_HFS_UTIL
1713 fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1714 #endif
1715 goto Return; // return FSUR_IO_FAIL
1716 }
1717
1718 /* Verify that it is an HFS+ volume. */
1719
1720 if (OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSPlusSigWord &&
1721 OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSXSigWord) {
1722 result = FSUR_IO_FAIL;
1723 #if TRACE_HFS_UTIL
1724 fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1725 #endif
1726 goto Return;
1727 }
1728
1729 blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
1730 catalogExtents = (HFSPlusExtentDescriptor *) malloc(sizeof(HFSPlusExtentRecord));
1731 if ( ! catalogExtents ) {
1732 result = FSUR_IO_FAIL;
1733 goto Return;
1734 }
1735 bcopy(volHdrPtr->catalogFile.extents, catalogExtents, sizeof(HFSPlusExtentRecord));
1736 catalogExtCount = kHFSPlusExtentDensity;
1737
1738 /* if there are overflow catalog extents, then go get them */
1739 if (OSSwapBigToHostInt32(catalogExtents[7].blockCount) != 0) {
1740 result = GetCatalogOverflowExtents(fd, hfsPlusVolumeOffset, volHdrPtr, &catalogExtents, &catalogExtCount);
1741 if (result != FSUR_IO_SUCCESS)
1742 goto Return;
1743 }
1744
1745 /* Read the header node of the catalog B-Tree */
1746
1747 result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
1748 catalogExtCount, catalogExtents,
1749 &catalogNodeSize, &leafNode);
1750 if (result != FSUR_IO_SUCCESS)
1751 goto Return;
1752
1753 /* Read the first leaf node of the catalog b-tree */
1754
1755 bufPtr = (char *)malloc(catalogNodeSize);
1756 if ( ! bufPtr ) {
1757 result = FSUR_IO_FAIL;
1758 goto Return;
1759 }
1760
1761 bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
1762
1763 result = ReadFile(fd, bufPtr, (off_t) leafNode * (off_t) catalogNodeSize, catalogNodeSize,
1764 hfsPlusVolumeOffset, blockSize,
1765 catalogExtCount, catalogExtents);
1766 if (result == FSUR_IO_FAIL) {
1767 #if TRACE_HFS_UTIL
1768 fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
1769 #endif
1770 goto Return; // return FSUR_IO_FAIL
1771 }
1772
1773 {
1774 u_int16_t * v;
1775 char * p;
1776 HFSPlusCatalogKey * k;
1777 CFStringRef cfstr;
1778
1779 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords) < 1) {
1780 result = FSUR_IO_FAIL;
1781 #if TRACE_HFS_UTIL
1782 fprintf(stderr, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1783 #endif
1784 goto Return;
1785 }
1786
1787 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1788
1789 p = bufPtr + catalogNodeSize - sizeof(u_int16_t); // pointer arithmetic in bytes
1790 v = (u_int16_t *)p;
1791
1792 // Get a pointer to the first record.
1793
1794 p = bufPtr + OSSwapBigToHostInt16(*v); // pointer arithmetic in bytes
1795 k = (HFSPlusCatalogKey *)p;
1796
1797 // There should be only one record whose parent is the root parent. It should be the first record.
1798
1799 if (OSSwapBigToHostInt32(k->parentID) != kHFSRootParentID) {
1800 result = FSUR_IO_FAIL;
1801 #if TRACE_HFS_UTIL
1802 fprintf(stderr, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1803 #endif
1804 goto Return;
1805 }
1806
1807 /* Extract the name of the root directory */
1808
1809 {
1810 HFSUniStr255 *swapped;
1811 int i;
1812
1813 swapped = (HFSUniStr255 *)malloc(sizeof(HFSUniStr255));
1814 if (swapped == NULL) {
1815 result = FSUR_IO_FAIL;
1816 goto Return;
1817 }
1818 swapped->length = OSSwapBigToHostInt16(k->nodeName.length);
1819
1820 for (i=0; i<swapped->length; i++) {
1821 swapped->unicode[i] = OSSwapBigToHostInt16(k->nodeName.unicode[i]);
1822 }
1823 swapped->unicode[i] = 0;
1824 cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, swapped->unicode, swapped->length);
1825 (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX, kCFStringEncodingUTF8);
1826 CFRelease(cfstr);
1827 free(swapped);
1828 }
1829 }
1830
1831 result = FSUR_IO_SUCCESS;
1832
1833 Return:
1834 if (volHdrPtr)
1835 free((char*) volHdrPtr);
1836
1837 if (catalogExtents)
1838 free((char*) catalogExtents);
1839
1840 if (bufPtr)
1841 free((char*)bufPtr);
1842
1843 return result;
1844
1845 } /* GetNameFromHFSPlusVolumeStartingAt */
1846
1847
1848 #pragma options align=mac68k
1849 typedef struct {
1850 BTNodeDescriptor node;
1851 BTHeaderRec header;
1852 } HeaderRec, *HeaderPtr;
1853 #pragma options align=reset
1854
1855 /*
1856 --
1857 --
1858 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1859 --
1860 */
1861 static int
1862 GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize,
1863 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
1864 u_int32_t *nodeSize, u_int32_t *firstLeafNode)
1865 {
1866 int result;
1867 HeaderRec * bTreeHeaderPtr = NULL;
1868
1869 bTreeHeaderPtr = (HeaderRec *) malloc(HFS_BLOCK_SIZE);
1870 if (bTreeHeaderPtr == NULL)
1871 return (FSUR_IO_FAIL);
1872
1873 /* Read the b-tree header node */
1874
1875 result = ReadFile(fd, bTreeHeaderPtr, 0, HFS_BLOCK_SIZE,
1876 hfsPlusVolumeOffset, blockSize,
1877 extentCount, extentList);
1878 if ( result == FSUR_IO_FAIL ) {
1879 #if TRACE_HFS_UTIL
1880 fprintf(stderr, "hfs.util: ERROR: reading header node failed\n");
1881 #endif
1882 goto free;
1883 }
1884
1885 if ( bTreeHeaderPtr->node.kind != kBTHeaderNode ) {
1886 result = FSUR_IO_FAIL;
1887 #if TRACE_HFS_UTIL
1888 fprintf(stderr, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1889 #endif
1890 goto free;
1891 }
1892
1893 *nodeSize = OSSwapBigToHostInt16(bTreeHeaderPtr->header.nodeSize);
1894
1895 if (OSSwapBigToHostInt32(bTreeHeaderPtr->header.leafRecords) == 0)
1896 *firstLeafNode = 0;
1897 else
1898 *firstLeafNode = OSSwapBigToHostInt32(bTreeHeaderPtr->header.firstLeafNode);
1899
1900 free:;
1901 free((char*) bTreeHeaderPtr);
1902
1903 return result;
1904
1905 } /* GetBTreeNodeInfo */
1906
1907
1908 /*
1909 --
1910 --
1911 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1912 --
1913 */
1914 static int
1915 GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset,
1916 HFSPlusVolumeHeader *volHdrPtr,
1917 HFSPlusExtentDescriptor **catalogExtents,
1918 u_int32_t *catalogExtCount)
1919 {
1920 off_t offset;
1921 u_int32_t numRecords;
1922 u_int32_t nodeSize;
1923 u_int32_t leafNode;
1924 u_int32_t blockSize;
1925 BTNodeDescriptor * bTreeNodeDescriptorPtr;
1926 HFSPlusExtentDescriptor * extents;
1927 size_t listsize;
1928 char * bufPtr = NULL;
1929 int i;
1930 int result;
1931
1932 blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
1933 listsize = *catalogExtCount * sizeof(HFSPlusExtentDescriptor);
1934 extents = *catalogExtents;
1935 offset = (off_t)OSSwapBigToHostInt32(volHdrPtr->extentsFile.extents[0].startBlock) *
1936 (off_t)blockSize;
1937
1938 /* Read the header node of the extents B-Tree */
1939
1940 result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
1941 kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents,
1942 &nodeSize, &leafNode);
1943 if (result != FSUR_IO_SUCCESS || leafNode == 0)
1944 goto Return;
1945
1946 /* Calculate the logical position of the first leaf node */
1947
1948 offset = (off_t) leafNode * (off_t) nodeSize;
1949
1950 /* Read the first leaf node of the extents b-tree */
1951
1952 bufPtr = (char *)malloc(nodeSize);
1953 if (! bufPtr) {
1954 result = FSUR_IO_FAIL;
1955 goto Return;
1956 }
1957
1958 bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
1959
1960 again:
1961 result = ReadFile(fd, bufPtr, offset, nodeSize,
1962 hfsPlusVolumeOffset, blockSize,
1963 kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents);
1964 if ( result == FSUR_IO_FAIL ) {
1965 #if TRACE_HFS_UTIL
1966 fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
1967 #endif
1968 goto Return;
1969 }
1970
1971 if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) {
1972 result = FSUR_IO_FAIL;
1973 goto Return;
1974 }
1975
1976 numRecords = OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords);
1977 for (i = 1; i <= numRecords; ++i) {
1978 u_int16_t * v;
1979 char * p;
1980 HFSPlusExtentKey * k;
1981
1982 /*
1983 * Get the offset (in bytes) of the record from the
1984 * list of offsets at the end of the node
1985 */
1986 p = bufPtr + nodeSize - (sizeof(u_int16_t) * i);
1987 v = (u_int16_t *)p;
1988
1989 /* Get a pointer to the record */
1990
1991 p = bufPtr + OSSwapBigToHostInt16(*v); /* pointer arithmetic in bytes */
1992 k = (HFSPlusExtentKey *)p;
1993
1994 if (OSSwapBigToHostInt32(k->fileID) != kHFSCatalogFileID)
1995 goto Return;
1996
1997 /* grow list and copy additional extents */
1998 listsize += sizeof(HFSPlusExtentRecord);
1999 extents = (HFSPlusExtentDescriptor *) realloc(extents, listsize);
2000 bcopy(p + OSSwapBigToHostInt16(k->keyLength) + sizeof(u_int16_t),
2001 &extents[*catalogExtCount], sizeof(HFSPlusExtentRecord));
2002
2003 *catalogExtCount += kHFSPlusExtentDensity;
2004 *catalogExtents = extents;
2005 }
2006
2007 if ((leafNode = OSSwapBigToHostInt32(bTreeNodeDescriptorPtr->fLink)) != 0) {
2008
2009 offset = (off_t) leafNode * (off_t) nodeSize;
2010
2011 goto again;
2012 }
2013
2014 Return:;
2015 if (bufPtr)
2016 free(bufPtr);
2017
2018 return (result);
2019 }
2020
2021
2022
2023 /*
2024 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2025 * position and number of contiguous bytes at that position.
2026 *
2027 * Inputs:
2028 * logicalOffset Logical offset in bytes from start of file
2029 * length Maximum number of bytes to map
2030 * blockSize Number of bytes per allocation block
2031 * extentCount Number of extents in file
2032 * extentList The file's extents
2033 *
2034 * Outputs:
2035 * physicalOffset Physical offset in bytes from start of volume
2036 * availableBytes Number of bytes physically contiguous (up to length)
2037 *
2038 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2039 */
2040 static int LogicalToPhysical(off_t offset, ssize_t length, u_int32_t blockSize,
2041 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
2042 off_t *physicalOffset, ssize_t *availableBytes)
2043 {
2044 off_t temp;
2045 u_int32_t logicalBlock;
2046 u_int32_t extent;
2047 u_int32_t blockCount = 0;
2048
2049 /* Determine allocation block containing logicalOffset */
2050 logicalBlock = offset / blockSize; /* This can't overflow for valid volumes */
2051 offset %= blockSize; /* Offset from start of allocation block */
2052
2053 /* Find the extent containing logicalBlock */
2054 for (extent = 0; extent < extentCount; ++extent)
2055 {
2056 blockCount = OSSwapBigToHostInt32(extentList[extent].blockCount);
2057
2058 if (blockCount == 0)
2059 return FSUR_IO_FAIL; /* Tried to map past physical end of file */
2060
2061 if (logicalBlock < blockCount)
2062 break; /* Found it! */
2063
2064 logicalBlock -= blockCount;
2065 }
2066
2067 if (extent >= extentCount)
2068 return FSUR_IO_FAIL; /* Tried to map past physical end of file */
2069
2070 /*
2071 * When we get here, extentList[extent] is the extent containing logicalOffset.
2072 * The desired allocation block is logicalBlock blocks into the extent.
2073 */
2074
2075 /* Compute the physical starting position */
2076 temp = OSSwapBigToHostInt32(extentList[extent].startBlock) + logicalBlock; /* First physical block */
2077 temp *= blockSize; /* Byte offset of first physical block */
2078 *physicalOffset = temp + offset;
2079
2080 /* Compute the available contiguous bytes. */
2081 temp = blockCount - logicalBlock; /* Number of blocks available in extent */
2082 temp *= blockSize;
2083 temp -= offset; /* Number of bytes available */
2084
2085 if (temp < length)
2086 *availableBytes = temp;
2087 else
2088 *availableBytes = length;
2089
2090 return FSUR_IO_SUCCESS;
2091 }
2092
2093
2094
2095 /*
2096 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2097 * ending position are not allocation or device block aligned.
2098 *
2099 * Inputs:
2100 * fd Descriptor for reading the volume
2101 * buffer The bytes are read into here
2102 * offset Offset in file to start reading
2103 * length Number of bytes to read
2104 * volOffset Byte offset from start of device to start of volume
2105 * blockSize Number of bytes per allocation block
2106 * extentCount Number of extents in file
2107 * extentList The file's exents
2108 *
2109 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2110 */
2111 static int ReadFile(int fd, void *buffer, off_t offset, ssize_t length,
2112 off_t volOffset, u_int32_t blockSize,
2113 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList)
2114 {
2115 int result = FSUR_IO_SUCCESS;
2116 off_t physOffset;
2117 ssize_t physLength;
2118
2119 while (length > 0)
2120 {
2121 result = LogicalToPhysical(offset, length, blockSize, extentCount, extentList,
2122 &physOffset, &physLength);
2123 if (result != FSUR_IO_SUCCESS)
2124 break;
2125
2126 result = readAt(fd, buffer, volOffset+physOffset, physLength);
2127 if (result != FSUR_IO_SUCCESS)
2128 break;
2129
2130 length -= physLength;
2131 offset += physLength;
2132 buffer = (char *) buffer + physLength;
2133 }
2134
2135 return result;
2136 }
2137
2138 /*
2139 -- readAt = lseek() + read()
2140 --
2141 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2142 --
2143 */
2144
2145 static ssize_t
2146 readAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2147 {
2148 int blocksize;
2149 off_t lseekResult;
2150 ssize_t readResult;
2151 void * rawData = NULL;
2152 off_t rawOffset;
2153 ssize_t rawLength;
2154 ssize_t dataOffset = 0;
2155 int result = FSUR_IO_SUCCESS;
2156
2157 if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2158 #if TRACE_HFS_UTIL
2159 fprintf(stderr, "hfs.util: readAt: couldn't determine block size of device.\n");
2160 #endif
2161 result = FSUR_IO_FAIL;
2162 goto Return;
2163 }
2164 /* put offset and length in terms of device blocksize */
2165 rawOffset = offset / blocksize * blocksize;
2166 dataOffset = offset - rawOffset;
2167 rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2168 rawData = malloc(rawLength);
2169 if (rawData == NULL) {
2170 result = FSUR_IO_FAIL;
2171 goto Return;
2172 }
2173
2174 lseekResult = lseek( fd, rawOffset, SEEK_SET );
2175 if ( lseekResult != rawOffset ) {
2176 result = FSUR_IO_FAIL;
2177 goto Return;
2178 }
2179
2180 readResult = read(fd, rawData, rawLength);
2181 if ( readResult != rawLength ) {
2182 #if TRACE_HFS_UTIL
2183 fprintf(stderr, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno);
2184 #endif
2185 result = FSUR_IO_FAIL;
2186 goto Return;
2187 }
2188 bcopy(rawData + dataOffset, bufPtr, length);
2189
2190 Return:
2191 if (rawData) {
2192 free(rawData);
2193 }
2194 return result;
2195
2196 } /* readAt */
2197
2198 /*
2199 -- writeAt = lseek() + write()
2200 --
2201 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2202 --
2203 */
2204
2205 static ssize_t
2206 writeAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2207 {
2208 int blocksize;
2209 off_t deviceoffset;
2210 ssize_t bytestransferred;
2211 void * rawData = NULL;
2212 off_t rawOffset;
2213 ssize_t rawLength;
2214 ssize_t dataOffset = 0;
2215 int result = FSUR_IO_SUCCESS;
2216
2217 if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2218 #if TRACE_HFS_UTIL
2219 fprintf(stderr, "hfs.util: couldn't determine block size of device.\n");
2220 #endif
2221 result = FSUR_IO_FAIL;
2222 goto Return;
2223 }
2224 /* put offset and length in terms of device blocksize */
2225 rawOffset = offset / blocksize * blocksize;
2226 dataOffset = offset - rawOffset;
2227 rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2228 rawData = malloc(rawLength);
2229 if (rawData == NULL) {
2230 result = FSUR_IO_FAIL;
2231 goto Return;
2232 }
2233
2234 deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2235 if ( deviceoffset != rawOffset ) {
2236 result = FSUR_IO_FAIL;
2237 goto Return;
2238 }
2239
2240 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2241 if (((rawOffset % blocksize) != 0) || ((rawLength % blocksize) != 0)) {
2242 bytestransferred = read(fd, rawData, rawLength);
2243 if ( bytestransferred != rawLength ) {
2244 #if TRACE_HFS_UTIL
2245 fprintf(stderr, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno);
2246 #endif
2247 result = FSUR_IO_FAIL;
2248 goto Return;
2249 }
2250 }
2251
2252 bcopy(bufPtr, rawData + dataOffset, length); /* Copy in the new data */
2253
2254 deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2255 if ( deviceoffset != rawOffset ) {
2256 result = FSUR_IO_FAIL;
2257 goto Return;
2258 }
2259
2260 bytestransferred = write(fd, rawData, rawLength);
2261 if ( bytestransferred != rawLength ) {
2262 #if TRACE_HFS_UTIL
2263 fprintf(stderr, "writeAt: attempt to write data to device failed?!");
2264 #endif
2265 result = FSUR_IO_FAIL;
2266 goto Return;
2267 }
2268
2269 Return:
2270 if (rawData) free(rawData);
2271
2272 return result;
2273
2274 } /* writeAt */
2275
2276
2277 /*
2278 * Get kernel's encoding bias.
2279 */
2280 static int
2281 GetEncodingBias()
2282 {
2283 int mib[3];
2284 size_t buflen = sizeof(int);
2285 struct vfsconf vfc;
2286 int hint = 0;
2287
2288 if (getvfsbyname("hfs", &vfc) < 0)
2289 goto error;
2290
2291 mib[0] = CTL_VFS;
2292 mib[1] = vfc.vfc_typenum;
2293 mib[2] = HFS_ENCODINGBIAS;
2294
2295 if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
2296 goto error;
2297 return (hint);
2298 error:
2299 return (-1);
2300 }
2301
2302 /******************************************************************************
2303 *
2304 * V O L U M E S T A T U S D A T A B A S E R O U T I N E S
2305 *
2306 *****************************************************************************/
2307
2308 #define DBHANDLESIGNATURE 0x75917737
2309
2310 /* Flag values for operation options: */
2311 #define DBMARKPOSITION 1
2312
2313 static char gVSDBPath[] = "/var/db/volinfo.database";
2314
2315 #define MAXIOMALLOC 16384
2316
2317 /* Database layout: */
2318
2319 struct VSDBKey {
2320 char uuid[16];
2321 };
2322
2323 struct VSDBRecord {
2324 char statusFlags[8];
2325 };
2326
2327 struct VSDBEntry {
2328 struct VSDBKey key;
2329 char keySeparator;
2330 char space;
2331 struct VSDBRecord record;
2332 char terminator;
2333 };
2334
2335 #define DBKEYSEPARATOR ':'
2336 #define DBBLANKSPACE ' '
2337 #define DBRECORDTERMINATOR '\n'
2338
2339 /* In-memory data structures: */
2340
2341 struct VSDBState {
2342 unsigned long signature;
2343 int dbfile;
2344 int dbmode;
2345 off_t recordPosition;
2346 };
2347
2348 typedef struct VSDBState *VSDBStatePtr;
2349
2350
2351
2352 /* Internal function prototypes: */
2353 static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
2354 static int UnlockDB(VSDBStatePtr dbstateptr);
2355
2356 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, unsigned long options);
2357 static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2358 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2359 static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2360 static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2);
2361
2362 static void FormatULong(unsigned long u, char *s);
2363 static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
2364 static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
2365 static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord);
2366 static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry);
2367 static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits);
2368
2369
2370
2371 /******************************************************************************
2372 *
2373 * P U B L I S H E D I N T E R F A C E R O U T I N E S
2374 *
2375 *****************************************************************************/
2376
2377 void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
2378 SHA_CTX context;
2379 char randomInputBuffer[26];
2380 unsigned char digest[20];
2381 time_t now;
2382 clock_t uptime;
2383 int mib[2];
2384 int sysdata;
2385 char sysctlstring[128];
2386 size_t datalen;
2387 double sysloadavg[3];
2388 struct vmtotal sysvmtotal;
2389
2390 do {
2391 /* Initialize the SHA-1 context for processing: */
2392 SHA1_Init(&context);
2393
2394 /* Now process successive bits of "random" input to seed the process: */
2395
2396 /* The current system's uptime: */
2397 uptime = clock();
2398 SHA1_Update(&context, &uptime, sizeof(uptime));
2399
2400 /* The kernel's boot time: */
2401 mib[0] = CTL_KERN;
2402 mib[1] = KERN_BOOTTIME;
2403 datalen = sizeof(sysdata);
2404 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2405 SHA1_Update(&context, &sysdata, datalen);
2406
2407 /* The system's host id: */
2408 mib[0] = CTL_KERN;
2409 mib[1] = KERN_HOSTID;
2410 datalen = sizeof(sysdata);
2411 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2412 SHA1_Update(&context, &sysdata, datalen);
2413
2414 /* The system's host name: */
2415 mib[0] = CTL_KERN;
2416 mib[1] = KERN_HOSTNAME;
2417 datalen = sizeof(sysctlstring);
2418 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2419 SHA1_Update(&context, sysctlstring, datalen);
2420
2421 /* The running kernel's OS release string: */
2422 mib[0] = CTL_KERN;
2423 mib[1] = KERN_OSRELEASE;
2424 datalen = sizeof(sysctlstring);
2425 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2426 SHA1_Update(&context, sysctlstring, datalen);
2427
2428 /* The running kernel's version string: */
2429 mib[0] = CTL_KERN;
2430 mib[1] = KERN_VERSION;
2431 datalen = sizeof(sysctlstring);
2432 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2433 SHA1_Update(&context, sysctlstring, datalen);
2434
2435 /* The system's load average: */
2436 datalen = sizeof(sysloadavg);
2437 getloadavg(sysloadavg, 3);
2438 SHA1_Update(&context, &sysloadavg, datalen);
2439
2440 /* The system's VM statistics: */
2441 mib[0] = CTL_VM;
2442 mib[1] = VM_METER;
2443 datalen = sizeof(sysvmtotal);
2444 sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
2445 SHA1_Update(&context, &sysvmtotal, datalen);
2446
2447 /* The current GMT (26 ASCII characters): */
2448 time(&now);
2449 strncpy(randomInputBuffer, asctime(gmtime(&now)), 26); /* "Mon Mar 27 13:46:26 2000" */
2450 SHA1_Update(&context, randomInputBuffer, 26);
2451
2452 /* Pad the accumulated input and extract the final digest hash: */
2453 SHA1_Final(digest, &context);
2454
2455 memcpy(newVolumeID, digest, sizeof(*newVolumeID));
2456 } while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
2457 }
2458
2459
2460
2461 void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) {
2462 int i;
2463 char c;
2464 unsigned long nextdigit;
2465 unsigned long high = 0;
2466 unsigned long low = 0;
2467 unsigned long carry;
2468
2469 for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) {
2470 if ((c >= '0') && (c <= '9')) {
2471 nextdigit = c - '0';
2472 } else if ((c >= 'A') && (c <= 'F')) {
2473 nextdigit = c - 'A' + 10;
2474 } else if ((c >= 'a') && (c <= 'f')) {
2475 nextdigit = c - 'a' + 10;
2476 } else {
2477 nextdigit = 0;
2478 }
2479 carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
2480 high = (high << 4) | carry;
2481 low = (low << 4) | nextdigit;
2482 }
2483
2484 volumeID->v.high = high;
2485 volumeID->v.low = low;
2486 }
2487
2488
2489
2490 void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
2491 FormatUUID(volumeID, UUIDString);
2492 *(UUIDString+16) = (char)0; /* Append a terminating null character */
2493 }
2494
2495
2496
2497 int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
2498 VSDBStatePtr dbstateptr;
2499
2500 *DBHandlePtr = NULL;
2501
2502 dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
2503 if (dbstateptr == NULL) {
2504 return ENOMEM;
2505 }
2506
2507 dbstateptr->dbmode = O_RDWR;
2508 dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2509 if (dbstateptr->dbfile == -1) {
2510 /*
2511 The file couldn't be opened for read/write access:
2512 try read-only access before giving up altogether.
2513 */
2514 dbstateptr->dbmode = O_RDONLY;
2515 dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2516 if (dbstateptr->dbfile == -1) {
2517 return errno;
2518 }
2519 }
2520
2521 dbstateptr->signature = DBHANDLESIGNATURE;
2522 *DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
2523 return 0;
2524 }
2525
2526
2527
2528 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) {
2529 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2530 struct VSDBEntry dbentry;
2531 int result;
2532
2533 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2534
2535 if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
2536
2537 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
2538 goto ErrExit;
2539 }
2540 *VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
2541
2542 result = 0;
2543
2544 ErrExit:
2545 UnlockDB(dbstateptr);
2546 return result;
2547 }
2548
2549
2550
2551 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) {
2552 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2553 struct VSDBEntry dbentry;
2554 int result;
2555
2556 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2557 if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
2558
2559 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2560
2561 FormatDBEntry(volumeID, VolumeStatus, &dbentry);
2562 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
2563 #if DEBUG_TRACE
2564 fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2565 #endif
2566 result = UpdateVolumeRecord(dbstateptr, &dbentry);
2567 } else if (result == -1) {
2568 #if DEBUG_TRACE
2569 fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2570 #endif
2571 result = AddVolumeRecord(dbstateptr, &dbentry);
2572 } else {
2573 goto ErrExit;
2574 }
2575
2576 fsync(dbstateptr->dbfile);
2577
2578 result = 0;
2579
2580 ErrExit:
2581 UnlockDB(dbstateptr);
2582 return result;
2583 }
2584
2585
2586
2587 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
2588 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2589 struct stat dbinfo;
2590 int result;
2591 unsigned long iobuffersize;
2592 void *iobuffer = NULL;
2593 off_t dataoffset;
2594 unsigned long iotransfersize;
2595 unsigned long bytestransferred;
2596
2597 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2598
2599 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2600
2601 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
2602 #if DEBUG_TRACE
2603 fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
2604 #endif
2605 if (result == -1) result = 0; /* Entry wasn't in the database to begin with? */
2606 goto StdEdit;
2607 } else {
2608 #if DEBUG_TRACE
2609 fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2610 #endif
2611 if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
2612 if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) {
2613 iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry);
2614 } else {
2615 iobuffersize = MAXIOMALLOC;
2616 }
2617 #if DEBUG_TRACE
2618 fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2619 (unsigned long)dbinfo.st_size, (unsigned long)dbstateptr->recordPosition);
2620 fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
2621 #endif
2622 if (iobuffersize > 0) {
2623 iobuffer = malloc(iobuffersize);
2624 if (iobuffer == NULL) {
2625 result = ENOMEM;
2626 goto ErrExit;
2627 }
2628
2629 dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
2630 do {
2631 iotransfersize = dbinfo.st_size - dataoffset;
2632 if (iotransfersize > 0) {
2633 if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
2634
2635 #if DEBUG_TRACE
2636 fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)dataoffset);
2637 #endif
2638 lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
2639 bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
2640 if (bytestransferred != iotransfersize) {
2641 result = errno;
2642 goto ErrExit;
2643 }
2644
2645 #if DEBUG_TRACE
2646 fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntry)));
2647 #endif
2648 lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
2649 bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
2650 if (bytestransferred != iotransfersize) {
2651 result = errno;
2652 goto ErrExit;
2653 }
2654
2655 dataoffset += (off_t)iotransfersize;
2656 }
2657 } while (iotransfersize > 0);
2658 }
2659 #if DEBUG_TRACE
2660 fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry))));
2661 #endif
2662 if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
2663 goto ErrExit;
2664 }
2665
2666 fsync(dbstateptr->dbfile);
2667
2668 result = 0;
2669 }
2670
2671 ErrExit:
2672 if (iobuffer) free(iobuffer);
2673 UnlockDB(dbstateptr);
2674
2675 StdEdit:
2676 return result;
2677 }
2678
2679
2680
2681 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
2682 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2683
2684 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2685
2686 dbstateptr->signature = 0;
2687
2688 close(dbstateptr->dbfile); /* Nothing we can do about any errors... */
2689 dbstateptr->dbfile = 0;
2690
2691 free(dbstateptr);
2692
2693 return 0;
2694 }
2695
2696
2697
2698 /******************************************************************************
2699 *
2700 * I N T E R N A L O N L Y D A T A B A S E R O U T I N E S
2701 *
2702 *****************************************************************************/
2703
2704 static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
2705 #if DEBUG_TRACE
2706 fprintf(stderr, "LockDB: Locking VSDB file...\n");
2707 #endif
2708 return flock(dbstateptr->dbfile, lockmode);
2709 }
2710
2711
2712
2713 static int UnlockDB(VSDBStatePtr dbstateptr) {
2714 #if DEBUG_TRACE
2715 fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
2716 #endif
2717 return flock(dbstateptr->dbfile, LOCK_UN);
2718 }
2719
2720
2721
2722 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) {
2723 struct VSDBKey searchkey;
2724 struct VSDBEntry dbentry;
2725 int result;
2726
2727 FormatDBKey(volumeID, &searchkey);
2728 lseek(dbstateptr->dbfile, 0, SEEK_SET);
2729
2730 do {
2731 result = GetVSDBEntry(dbstateptr, &dbentry);
2732 if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
2733 if (targetEntry != NULL) {
2734 #if DEBUG_TRACE
2735 fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry);
2736 #endif
2737 memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
2738 }
2739 return 0;
2740 }
2741 } while (result == 0);
2742
2743 return -1;
2744 }
2745
2746
2747
2748 static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) {
2749 #if DEBUG_TRACE
2750 VolumeUUIDString id;
2751 #endif
2752
2753 #if DEBUG_TRACE
2754 strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2755 id[sizeof(dbentry->key.uuid)] = (char)0;
2756 fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id);
2757 #endif
2758 lseek(dbstateptr->dbfile, 0, SEEK_END);
2759 return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
2760 }
2761
2762
2763
2764
2765 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2766 #if DEBUG_TRACE
2767 VolumeUUIDString id;
2768 #endif
2769
2770 #if DEBUG_TRACE
2771 strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2772 id[sizeof(dbentry->key.uuid)] = (char)0;
2773 fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (unsigned long)dbstateptr->recordPosition);
2774 #endif
2775 lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET);
2776 #if DEBUG_TRACE
2777 fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry));
2778 #endif
2779 return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry));
2780 }
2781
2782
2783
2784 static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2785 struct VSDBEntry entry;
2786 int result;
2787 #if DEBUG_TRACE
2788 VolumeUUIDString id;
2789 #endif
2790
2791 dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR);
2792 #if 0 // DEBUG_TRACE
2793 fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr->recordPosition);
2794 #endif
2795 result = read(dbstateptr->dbfile, &entry, sizeof(entry));
2796 if ((result != sizeof(entry)) ||
2797 (entry.keySeparator != DBKEYSEPARATOR) ||
2798 (entry.space != DBBLANKSPACE) ||
2799 (entry.terminator != DBRECORDTERMINATOR)) {
2800 return -1;
2801 }
2802
2803 #if DEBUG_TRACE
2804 strncpy(id, entry.key.uuid, sizeof(entry.key.uuid));
2805 id[sizeof(entry.key.uuid)] = (char)0;
2806 fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id);
2807 #endif
2808 memcpy(dbentry, &entry, sizeof(*dbentry));
2809 return 0;
2810 }
2811
2812
2813
2814 static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
2815 #if 0 // DEBUG_TRACE
2816 VolumeUUIDString id;
2817
2818 strncpy(id, key1->uuid, sizeof(key1->uuid));
2819 id[sizeof(key1->uuid)] = (char)0;
2820 fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id);
2821 strncpy(id, key2->uuid, sizeof(key2->uuid));
2822 id[sizeof(key2->uuid)] = (char)0;
2823 fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid));
2824 #endif
2825
2826 return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
2827 }
2828
2829
2830
2831 /******************************************************************************
2832 *
2833 * F O R M A T T I N G A N D C O N V E R S I O N R O U T I N E S
2834 *
2835 *****************************************************************************/
2836
2837 static void FormatULong(unsigned long u, char *s) {
2838 unsigned long d;
2839 int i;
2840 char *digitptr = s;
2841
2842 for (i = 0; i < 8; ++i) {
2843 d = ((u & 0xF0000000) >> 28) & 0x0000000F;
2844 if (d < 10) {
2845 *digitptr++ = (char)(d + '0');
2846 } else {
2847 *digitptr++ = (char)(d - 10 + 'A');
2848 }
2849 u = u << 4;
2850 }
2851 }
2852
2853
2854
2855 static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) {
2856 FormatULong(volumeID->v.high, UUIDField);
2857 FormatULong(volumeID->v.low, UUIDField+8);
2858
2859 }
2860
2861
2862
2863 static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
2864 FormatUUID(volumeID, dbkey->uuid);
2865 }
2866
2867
2868
2869 static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord) {
2870 FormatULong(volumeStatusFlags, dbrecord->statusFlags);
2871 }
2872
2873
2874
2875 static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry) {
2876 FormatDBKey(volumeID, &dbentry->key);
2877 dbentry->keySeparator = DBKEYSEPARATOR;
2878 dbentry->space = DBBLANKSPACE;
2879 FormatDBRecord(volumeStatusFlags, &dbentry->record);
2880 #if 0 // DEBUG_TRACE
2881 dbentry->terminator = (char)0;
2882 fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry));
2883 #endif
2884 dbentry->terminator = DBRECORDTERMINATOR;
2885 }
2886
2887
2888
2889 static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) {
2890 int i;
2891 char c;
2892 unsigned long nextdigit;
2893 unsigned long n;
2894
2895 n = 0;
2896 for (i = 0; (i < 8) && ((c = hs[i]) != (char)0) ; ++i) {
2897 if ((c >= '0') && (c <= '9')) {
2898 nextdigit = c - '0';
2899 } else if ((c >= 'A') && (c <= 'F')) {
2900 nextdigit = c - 'A' + 10;
2901 } else if ((c >= 'a') && (c <= 'f')) {
2902 nextdigit = c - 'a' + 10;
2903 } else {
2904 nextdigit = 0;
2905 }
2906 n = (n << 4) + nextdigit;
2907 }
2908
2909 return n;
2910 }