]> git.saurik.com Git - hfs.git/blob - hfs_util/hfsutil_main.c
hfs-165.1.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 * rawDeviceNamePtr, char * blockDeviceNamePtr );
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, blockDeviceName);
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 /*
657 PrintVolumeNameAttr
658
659 Get the volume name of the volume mounted at "path". Print that volume
660 name to standard out.
661
662 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
663 */
664 struct VolumeNameBuf {
665 u_int32_t info_length;
666 attrreference_t name_ref;
667 char buffer[1024];
668 };
669
670 static int
671 PrintVolumeNameAttr(const char *path)
672 {
673 struct attrlist alist;
674 struct VolumeNameBuf volNameInfo;
675 int result;
676
677 /* Set up the attrlist structure to get the volume's Finder Info */
678 alist.bitmapcount = 5;
679 alist.reserved = 0;
680 alist.commonattr = 0;
681 alist.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
682 alist.dirattr = 0;
683 alist.fileattr = 0;
684 alist.forkattr = 0;
685
686 /* Get the Finder Info */
687 result = getattrlist(path, &alist, &volNameInfo, sizeof(volNameInfo), 0);
688 if (result) {
689 result = FSUR_IO_FAIL;
690 goto Err_Exit;
691 }
692
693 /* Print the name to standard out */
694 printf("%.*s", (int) volNameInfo.name_ref.attr_length, ((char *) &volNameInfo.name_ref) + volNameInfo.name_ref.attr_dataoffset);
695 result = FSUR_RECOGNIZED;
696
697 Err_Exit:
698 return result;
699 }
700
701
702 /* ******************************************* DoProbe **********************************************
703 Purpose -
704 This routine will open the given device and check to make sure there is media that looks
705 like an HFS. If it is HFS, then print the volume name to standard output.
706 Input -
707 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
708 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
709 Output -
710 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
711 *************************************************************************************************** */
712 static int
713 DoProbe(char *rawDeviceNamePtr, char *blockDeviceNamePtr)
714 {
715 int result = FSUR_UNRECOGNIZED;
716 int fd = 0;
717 char * bufPtr;
718 HFSMasterDirectoryBlock * mdbPtr;
719 HFSPlusVolumeHeader * volHdrPtr;
720 u_char volnameUTF8[NAME_MAX+1];
721
722 /*
723 * Determine if there is a volume already mounted from this device. If
724 * there is, and it is HFS, then we need to get the volume name via
725 * getattrlist.
726 *
727 * NOTE: We're using bufPtr to hold a pointer to a path.
728 */
729 bufPtr = NULL;
730 result = GetHFSMountPoint(blockDeviceNamePtr, &bufPtr);
731 if (result != FSUR_IO_SUCCESS) {
732 goto Err_Exit;
733 }
734 if (bufPtr != NULL) {
735 /* There is an HFS volume mounted from the device. */
736 result = PrintVolumeNameAttr(bufPtr);
737 goto Err_Exit;
738 }
739
740 /*
741 * If we get here, there is no volume mounted from this device, so
742 * go probe the raw device directly.
743 */
744
745 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
746 if ( ! bufPtr ) {
747 result = FSUR_UNRECOGNIZED;
748 goto Return;
749 }
750
751 mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
752 volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
753
754 fd = open( rawDeviceNamePtr, O_RDONLY, 0 );
755 if( fd <= 0 ) {
756 result = FSUR_IO_FAIL;
757 goto Return;
758 }
759
760 /*
761 * Read the HFS Master Directory Block from sector 2
762 */
763 result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
764 if (FSUR_IO_FAIL == result)
765 goto Return;
766
767 /* get classic HFS volume name (from MDB) */
768 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
769 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) {
770 Boolean cfOK;
771 CFStringRef cfstr;
772 CFStringEncoding encoding;
773
774 /* Some poorly mastered HFS CDs have an empty MDB name field! */
775 if (mdbPtr->drVN[0] == '\0') {
776 strcpy((char *)&mdbPtr->drVN[1], gHFS_FS_NAME_NAME);
777 mdbPtr->drVN[0] = strlen(gHFS_FS_NAME_NAME);
778 }
779
780 /* Check for an encoding hint in the Finder Info (field 4). */
781 encoding = GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr->drFndrInfo[4]));
782 if (encoding == kCFStringEncodingInvalidId) {
783 /* Next try the encoding bias in the kernel. */
784 encoding = GetEncodingBias();
785 if (encoding == 0 || encoding == kCFStringEncodingInvalidId)
786 encoding = __CFStringGetDefaultEncodingForHFSUtil();
787 }
788
789 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
790 mdbPtr->drVN, encoding);
791 cfOK = _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
792 CFRelease(cfstr);
793
794 if (!cfOK && encoding != kCFStringEncodingMacRoman) {
795
796 /* default to MacRoman on conversion errors */
797 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
798 mdbPtr->drVN, kCFStringEncodingMacRoman);
799 _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
800 CFRelease(cfstr);
801 encoding = kCFStringEncodingMacRoman;
802 }
803
804 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
805 if (encoding != kCFStringEncodingMacRoman) {
806 if (load_encoding(encoding) != FSUR_IO_SUCCESS) {
807 encoding = kCFStringEncodingMacRoman;
808 cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, encoding);
809 _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
810 CFRelease(cfstr);
811 }
812 }
813
814 /* get HFS Plus volume name (from Catalog) */
815 } else if ((OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord) ||
816 (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) ||
817 (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
818 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord)) {
819 off_t startOffset;
820
821 if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSSigWord) {
822 /* embedded volume, first find offset */
823 result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset);
824 if ( result != FSUR_IO_SUCCESS )
825 goto Return;
826 } else {
827 startOffset = 0;
828 }
829
830 result = GetNameFromHFSPlusVolumeStartingAt(fd, startOffset,
831 volnameUTF8);
832 } else {
833 result = FSUR_UNRECOGNIZED;
834 }
835
836 if (FSUR_IO_SUCCESS == result) {
837 unsigned char *s;
838
839 /* Change slashes to colons in the volume name */
840 for (s=volnameUTF8; *s; ++s) {
841 if (*s == '/')
842 *s = ':';
843 }
844
845 /* Print the volume name to standard output */
846 write(1, volnameUTF8, strlen((char *)volnameUTF8));
847 result = FSUR_RECOGNIZED;
848 }
849
850 Return:
851
852 if ( bufPtr )
853 free( bufPtr );
854
855 if (fd > 0)
856 close(fd);
857 Err_Exit:
858 return result;
859
860 } /* DoProbe */
861
862 /*
863 * Create a version 3 UUID from a unique "name" in the given "name space".
864 * Version 3 UUID are derived using "name" via MD5 checksum.
865 *
866 * Parameters:
867 * result_uuid - resulting UUID.
868 * namespace - namespace in which given name exists and UUID should be created.
869 * name - unique string used to create version 3 UUID.
870 * namelen - length of the name string.
871 */
872 static void
873 uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen)
874 {
875 MD5_CTX c;
876
877 MD5_Init(&c);
878 MD5_Update(&c, namespace, sizeof(uuid_t));
879 MD5_Update(&c, name, namelen);
880 MD5_Final(result_uuid, &c);
881
882 result_uuid[6] = (result_uuid[6] & 0x0F) | 0x30;
883 result_uuid[8] = (result_uuid[8] & 0x3F) | 0x80;
884 }
885
886
887 /* **************************************** DoGetUUIDKey *******************************************
888 Purpose -
889 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
890 Input -
891 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
892 Output -
893 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
894 *************************************************************************************************** */
895 static int
896 DoGetUUIDKey( const char * theDeviceNamePtr ) {
897 int result;
898 VolumeUUID targetVolumeUUID;
899 uuid_t uuid;
900 char uuidLine[40];
901
902 unsigned char rawUUID[8];
903
904 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) goto Err_Exit;
905
906 ((uint32_t *)rawUUID)[0] = OSSwapHostToBigInt32(targetVolumeUUID.v.high);
907 ((uint32_t *)rawUUID)[1] = OSSwapHostToBigInt32(targetVolumeUUID.v.low);
908
909 uuid_create_md5_from_name(uuid, kFSUUIDNamespaceSHA1, rawUUID, sizeof(rawUUID));
910 uuid_unparse(uuid, uuidLine);
911 write(1, uuidLine, strlen(uuidLine));
912 result = FSUR_IO_SUCCESS;
913
914 Err_Exit:
915 return result;
916 }
917
918
919
920 /* *************************************** DoChangeUUIDKey ******************************************
921 Purpose -
922 This routine will change the UUID on the specified block device.
923 Input -
924 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
925 Output -
926 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
927 *************************************************************************************************** */
928 static int
929 DoChangeUUIDKey( const char * theDeviceNamePtr ) {
930 int result;
931 VolumeUUID newVolumeUUID;
932
933 GenerateVolumeUUID(&newVolumeUUID);
934 result = SetVolumeUUID(theDeviceNamePtr, &newVolumeUUID);
935
936 return result;
937 }
938
939
940
941 /* **************************************** DoAdopt *******************************************
942 Purpose -
943 This routine will add the UUID of the specified block device to the list of local volumes.
944 Input -
945 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
946 Output -
947 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
948 *************************************************************************************************** */
949 static int
950 DoAdopt( const char * theDeviceNamePtr ) {
951 int result, closeresult;
952 VolumeUUID targetVolumeUUID;
953 VolumeStatusDBHandle vsdbhandle = NULL;
954 unsigned long targetVolumeStatus;
955
956 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
957
958 if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
959 if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
960 targetVolumeStatus = 0;
961 }
962 targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
963 if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
964
965 result = FSUR_IO_SUCCESS;
966
967 Err_Exit:
968 if (vsdbhandle) {
969 closeresult = CloseVolumeStatusDB(vsdbhandle);
970 vsdbhandle = NULL;
971 if (result == FSUR_IO_SUCCESS) result = closeresult;
972 }
973
974 if ((result != 0) && (result != FSUR_IO_SUCCESS)) result = FSUR_IO_FAIL;
975
976 Err_Return:
977 #if TRACE_HFS_UTIL
978 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoAdopt: returning %d...\n", result);
979 #endif
980 return result;
981 }
982
983
984
985 /* **************************************** DoDisown *******************************************
986 Purpose -
987 This routine will change the status of the specified block device to ignore its permissions.
988 Input -
989 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
990 Output -
991 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
992 *************************************************************************************************** */
993 static int
994 DoDisown( const char * theDeviceNamePtr ) {
995 int result, closeresult;
996 VolumeUUID targetVolumeUUID;
997 VolumeStatusDBHandle vsdbhandle = NULL;
998 unsigned long targetVolumeStatus;
999
1000 if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
1001
1002 if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
1003 if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
1004 targetVolumeStatus = 0;
1005 }
1006 targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
1007 if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
1008
1009 result = FSUR_IO_SUCCESS;
1010
1011 Err_Exit:
1012 if (vsdbhandle) {
1013 closeresult = CloseVolumeStatusDB(vsdbhandle);
1014 vsdbhandle = NULL;
1015 if (result == FSUR_IO_SUCCESS) result = closeresult;
1016 }
1017
1018 if ((result != 0) && (result != FSUR_IO_SUCCESS)) {
1019 #if TRACE_HFS_UTIL
1020 if (result != 0) fprintf(stderr, "DoDisown: result = %d; changing to %d...\n", result, FSUR_IO_FAIL);
1021 #endif
1022 result = FSUR_IO_FAIL;
1023 }
1024
1025 Err_Return:
1026 #if TRACE_HFS_UTIL
1027 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoDisown: returning %d...\n", result);
1028 #endif
1029 return result;
1030 }
1031
1032
1033 static int
1034 get_multiplier(char c)
1035 {
1036 if (tolower(c) == 'k') {
1037 return 1024;
1038 } else if (tolower(c) == 'm') {
1039 return 1024 * 1024;
1040 } else if (tolower(c) == 'g') {
1041 return 1024 * 1024 * 1024;
1042 }
1043
1044 return 1;
1045 }
1046
1047 /* **************************************** ParseArgs ********************************************
1048 Purpose -
1049 This routine will make sure the arguments passed in to us are cool.
1050 Here is how this utility is used:
1051
1052 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1053 actionArg:
1054 -p (Probe for mounting)
1055 -P (Probe for initializing - not supported)
1056 -m (Mount)
1057 -r (Repair - not supported)
1058 -u (Unmount)
1059 -M (Force Mount)
1060 -i (Initialize - not supported)
1061
1062 deviceArg:
1063 disk0s2 (for example)
1064
1065 mountPointArg:
1066 /foo/bar/ (required for Mount and Force Mount actions)
1067
1068 flagsArg:
1069 (these are ignored for CDROMs)
1070 either "readonly" OR "writable"
1071 either "removable" OR "fixed"
1072 either "nosuid" or "suid"
1073 either "nodev" or "dev"
1074
1075 examples:
1076 hfs.util -p disk0s2 removable writable
1077 hfs.util -p disk0s2 removable readonly
1078 hfs.util -m disk0s2 /my/hfs
1079
1080 Input -
1081 argc - the number of arguments in argv.
1082 argv - array of arguments.
1083 Output -
1084 returns FSUR_INVAL if we find a bad argument else 0.
1085 *************************************************************************************************** */
1086 static int
1087 ParseArgs(int argc, const char *argv[], const char ** actionPtr,
1088 const char ** mountPointPtr, boolean_t * isEjectablePtr,
1089 boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr)
1090 {
1091 int result = FSUR_INVAL;
1092 int deviceLength, doLengthCheck = 1;
1093 int index;
1094 int mounting = 0;
1095
1096 /* Must have at least 3 arguments and the action argument must start with a '-' */
1097 if ( (argc < 3) || (argv[1][0] != '-') ) {
1098 DoDisplayUsage( argv );
1099 goto Return;
1100 }
1101
1102 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1103
1104 * actionPtr = & argv[1][1];
1105
1106 switch ( argv[1][1] ) {
1107 case FSUC_PROBE:
1108 /* action Probe and requires 5 arguments (need the flags) */
1109 if ( argc < 5 ) {
1110 DoDisplayUsage( argv );
1111 goto Return;
1112 } else {
1113 index = 3;
1114 }
1115 break;
1116
1117 case FSUC_UNMOUNT:
1118 /* Note: the device argument in argv[2] is checked further down but ignored. */
1119 * mountPointPtr = argv[3];
1120 index = 0; /* No isEjectable/isLocked flags for unmount. */
1121 break;
1122
1123 case FSUC_MOUNT:
1124 case FSUC_MOUNT_FORCE:
1125 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1126 if ( argc < 8 ) {
1127 DoDisplayUsage( argv );
1128 goto Return;
1129 } else {
1130 * mountPointPtr = argv[3];
1131 index = 4;
1132 mounting = 1;
1133 }
1134 break;
1135
1136 case FSUC_GETUUID:
1137 index = 0;
1138 break;
1139
1140 case FSUC_SETUUID:
1141 index = 0;
1142 break;
1143
1144 case FSUC_ADOPT:
1145 index = 0;
1146 break;
1147
1148 case FSUC_DISOWN:
1149 index = 0;
1150 break;
1151
1152 // XXXdbg
1153 case FSUC_MKJNL:
1154 index = 0;
1155 doLengthCheck = 0;
1156 if (isdigit(argv[2][0])) {
1157 char *ptr;
1158 gJournalSize = strtoul(argv[2], &ptr, 0);
1159 if (ptr) {
1160 gJournalSize *= get_multiplier(*ptr);
1161 }
1162 return 0;
1163 }
1164 break;
1165
1166 case FSUC_UNJNL:
1167 index = 0;
1168 doLengthCheck = 0;
1169 break;
1170
1171 case FSUC_UNJNL_RAW:
1172 index = 0;
1173 doLengthCheck = 0;
1174 break;
1175
1176 case FSUC_JNLINFO:
1177 index = 0;
1178 doLengthCheck = 0;
1179 break;
1180 // XXXdbg
1181
1182 default:
1183 DoDisplayUsage( argv );
1184 goto Return;
1185 break;
1186 }
1187
1188 /* Make sure device (argv[2]) is something reasonable */
1189 deviceLength = strlen( argv[2] );
1190 if ( doLengthCheck && (deviceLength < 3 || deviceLength > NAME_MAX) ) {
1191 DoDisplayUsage( argv );
1192 goto Return;
1193 }
1194
1195 if ( index ) {
1196 /* Flags: removable/fixed. */
1197 if ( 0 == strcmp(argv[index],"removable") ) {
1198 * isEjectablePtr = 1;
1199 } else if ( 0 == strcmp(argv[index],"fixed") ) {
1200 * isEjectablePtr = 0;
1201 } else {
1202 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index,argv[index]);
1203 }
1204
1205 /* Flags: readonly/writable. */
1206 if ( 0 == strcmp(argv[index+1],"readonly") ) {
1207 * isLockedPtr = 1;
1208 } else if ( 0 == strcmp(argv[index+1],"writable") ) {
1209 * isLockedPtr = 0;
1210 } else {
1211 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index,argv[index+1]);
1212 }
1213
1214 if (mounting) {
1215 /* Flags: suid/nosuid. */
1216 if ( 0 == strcmp(argv[index+2],"suid") ) {
1217 * isSetuidPtr = 1;
1218 } else if ( 0 == strcmp(argv[index+2],"nosuid") ) {
1219 * isSetuidPtr = 0;
1220 } else {
1221 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index,argv[index+2]);
1222 }
1223
1224 /* Flags: dev/nodev. */
1225 if ( 0 == strcmp(argv[index+3],"dev") ) {
1226 * isDevPtr = 1;
1227 } else if ( 0 == strcmp(argv[index+3],"nodev") ) {
1228 * isDevPtr = 0;
1229 } else {
1230 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index,argv[index+3]);
1231 }
1232 }
1233
1234
1235 }
1236
1237 result = 0;
1238
1239 Return:
1240 return result;
1241
1242 } /* ParseArgs */
1243
1244
1245 /* *************************************** DoDisplayUsage ********************************************
1246 Purpose -
1247 This routine will do a printf of the correct usage for this utility.
1248 Input -
1249 argv - array of arguments.
1250 Output -
1251 NA.
1252 *************************************************************************************************** */
1253 static void
1254 DoDisplayUsage(const char *argv[])
1255 {
1256 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0]);
1257 printf("action_arg:\n");
1258 printf(" -%c (Probe for mounting)\n", FSUC_PROBE);
1259 printf(" -%c (Mount)\n", FSUC_MOUNT);
1260 printf(" -%c (Unmount)\n", FSUC_UNMOUNT);
1261 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE);
1262 #ifdef HFS_UUID_SUPPORT
1263 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID);
1264 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID);
1265 #endif HFS_UUID_SUPPORT
1266 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT);
1267 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL);
1268 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL);
1269 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW);
1270 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO);
1271 printf("device_arg:\n");
1272 printf(" device we are acting upon (for example, 'disk0s2')\n");
1273 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL, FSUC_UNJNL);
1274 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1275 printf("mount_point_arg:\n");
1276 printf(" required for Mount and Force Mount \n");
1277 printf("Flags:\n");
1278 printf(" required for Mount, Force Mount and Probe\n");
1279 printf(" indicates removable or fixed (for example 'fixed')\n");
1280 printf(" indicates readonly or writable (for example 'readonly')\n");
1281 printf(" indicates suid or nosuid (for example 'suid')\n");
1282 printf(" indicates dev or nodev (for example 'dev')\n");
1283 printf("Examples:\n");
1284 printf(" %s -p disk0s2 fixed writable\n", argv[0]);
1285 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv[0]);
1286
1287 return;
1288
1289 } /* DoDisplayUsage */
1290
1291
1292 /*
1293 GetHFSMountPoint
1294
1295 Given a path to a device, determine if a volume is mounted on that
1296 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1297 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1298 no volume mounted on the device, set *pathPtr to NULL and return
1299 FSUR_IO_SUCCESS.
1300
1301 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1302 */
1303 static int
1304 GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr)
1305 {
1306 int result;
1307 int i, numMounts;
1308 struct statfs *buf;
1309
1310 /* Assume no mounted volume found */
1311 *pathPtr = NULL;
1312 result = FSUR_IO_SUCCESS;
1313
1314 numMounts = getmntinfo(&buf, MNT_NOWAIT);
1315 if (numMounts == 0)
1316 return FSUR_IO_FAIL;
1317
1318 for (i=0; i<numMounts; ++i) {
1319 if (!strcmp(deviceNamePtr, buf[i].f_mntfromname)) {
1320 /* Found a mounted volume; check the type */
1321 if (!strcmp(buf[i].f_fstypename, "hfs")) {
1322 *pathPtr = buf[i].f_mntonname;
1323 /* result = FSUR_IO_SUCCESS, above */
1324 } else {
1325 result = FSUR_UNRECOGNIZED;
1326 }
1327 break;
1328 }
1329 }
1330
1331 return result;
1332 }
1333
1334
1335 /*
1336 ReadHeaderBlock
1337
1338 Read the Master Directory Block or Volume Header Block from an HFS,
1339 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1340 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1341 Return a pointer to the volume UUID in the Finder Info.
1342
1343 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1344 */
1345 static int
1346 ReadHeaderBlock(int fd, void *bufPtr, off_t *startOffset, VolumeUUID **finderInfoUUIDPtr)
1347 {
1348 int result;
1349 HFSMasterDirectoryBlock * mdbPtr;
1350 HFSPlusVolumeHeader * volHdrPtr;
1351
1352 mdbPtr = bufPtr;
1353 volHdrPtr = bufPtr;
1354
1355 /*
1356 * Read the HFS Master Directory Block or Volume Header from sector 2
1357 */
1358 *startOffset = 0;
1359 result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1360 if (result != FSUR_IO_SUCCESS)
1361 goto Err_Exit;
1362
1363 /*
1364 * If this is a wrapped HFS Plus volume, read the Volume Header from
1365 * sector 2 of the embedded volume.
1366 */
1367 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
1368 OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) {
1369 result = GetEmbeddedHFSPlusVol(mdbPtr, startOffset);
1370 if (result != FSUR_IO_SUCCESS)
1371 goto Err_Exit;
1372 result = readAt(fd, bufPtr, *startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1373 if (result != FSUR_IO_SUCCESS)
1374 goto Err_Exit;
1375 }
1376
1377 /*
1378 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1379 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1380 * UUID from the Finder Info.
1381 */
1382 if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) {
1383 *finderInfoUUIDPtr = (VolumeUUID *)(&mdbPtr->drFndrInfo[6]);
1384 } else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord ||
1385 OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) {
1386 *finderInfoUUIDPtr = (VolumeUUID *)&volHdrPtr->finderInfo[24];
1387 } else {
1388 result = FSUR_UNRECOGNIZED;
1389 }
1390
1391 Err_Exit:
1392 return result;
1393 }
1394
1395
1396 /*
1397 GetVolumeUUIDRaw
1398
1399 Read the UUID from an unmounted volume, by doing direct access to the device.
1400 Assumes the caller has already determined that a volume is not mounted
1401 on the device.
1402
1403 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1404 */
1405 static int
1406 GetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr)
1407 {
1408 int fd = 0;
1409 char * bufPtr;
1410 off_t startOffset;
1411 VolumeUUID *finderInfoUUIDPtr;
1412 int result;
1413
1414 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1415 if ( ! bufPtr ) {
1416 result = FSUR_UNRECOGNIZED;
1417 goto Err_Exit;
1418 }
1419
1420 fd = open( deviceNamePtr, O_RDONLY, 0);
1421 if (fd <= 0) {
1422 #if TRACE_HFS_UTIL
1423 fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno);
1424 #endif
1425 result = FSUR_IO_FAIL;
1426 goto Err_Exit;
1427 }
1428
1429 /*
1430 * Get the pointer to the volume UUID in the Finder Info
1431 */
1432 result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1433 if (result != FSUR_IO_SUCCESS)
1434 goto Err_Exit;
1435
1436 /*
1437 * Copy the volume UUID out of the Finder Info
1438 */
1439 volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1440 volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1441
1442 Err_Exit:
1443 if (fd > 0) close(fd);
1444 if (bufPtr) free(bufPtr);
1445
1446 #if TRACE_HFS_UTIL
1447 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result);
1448 #endif
1449 return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1450 }
1451
1452
1453 /*
1454 SetVolumeUUIDRaw
1455
1456 Write a previously generated UUID to an unmounted volume, by doing direct
1457 access to the device. Assumes the caller has already determined that a
1458 volume is not mounted on the device.
1459
1460 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1461 */
1462 static int
1463 SetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr)
1464 {
1465 int fd = 0;
1466 char * bufPtr;
1467 off_t startOffset;
1468 VolumeUUID *finderInfoUUIDPtr;
1469 int result;
1470
1471 bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1472 if ( ! bufPtr ) {
1473 result = FSUR_UNRECOGNIZED;
1474 goto Err_Exit;
1475 }
1476
1477 fd = open( deviceNamePtr, O_RDWR, 0);
1478 if (fd <= 0) {
1479 #if TRACE_HFS_UTIL
1480 fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno);
1481 #endif
1482 result = FSUR_IO_FAIL;
1483 goto Err_Exit;
1484 }
1485
1486 /*
1487 * Get the pointer to the volume UUID in the Finder Info
1488 */
1489 result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1490 if (result != FSUR_IO_SUCCESS)
1491 goto Err_Exit;
1492
1493 /*
1494 * Update the UUID in the Finder Info
1495 */
1496 finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1497 finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1498
1499 /*
1500 * Write the modified MDB or VHB back to disk
1501 */
1502 result = writeAt(fd, bufPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1503
1504 Err_Exit:
1505 if (fd > 0) close(fd);
1506 if (bufPtr) free(bufPtr);
1507
1508 #if TRACE_HFS_UTIL
1509 if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result);
1510 #endif
1511 return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1512 }
1513
1514
1515 /*
1516 GetVolumeUUIDAttr
1517
1518 Read the UUID from a mounted volume, by calling getattrlist().
1519 Assumes the path is the mount point of an HFS volume.
1520
1521 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1522 */
1523 static int
1524 GetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1525 {
1526 struct attrlist alist;
1527 struct FinderAttrBuf volFinderInfo;
1528 VolumeUUID *finderInfoUUIDPtr;
1529 int result;
1530
1531 /* Set up the attrlist structure to get the volume's Finder Info */
1532 alist.bitmapcount = 5;
1533 alist.reserved = 0;
1534 alist.commonattr = ATTR_CMN_FNDRINFO;
1535 alist.volattr = ATTR_VOL_INFO;
1536 alist.dirattr = 0;
1537 alist.fileattr = 0;
1538 alist.forkattr = 0;
1539
1540 /* Get the Finder Info */
1541 result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1542 if (result) {
1543 result = FSUR_IO_FAIL;
1544 goto Err_Exit;
1545 }
1546
1547 /* Copy the UUID from the Finder Into to caller's buffer */
1548 finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1549 volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1550 volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1551 result = FSUR_IO_SUCCESS;
1552
1553 Err_Exit:
1554 return result;
1555 }
1556
1557
1558 /*
1559 SetVolumeUUIDAttr
1560
1561 Write a UUID to a mounted volume, by calling setattrlist().
1562 Assumes the path is the mount point of an HFS volume.
1563
1564 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1565 */
1566 static int
1567 SetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1568 {
1569 struct attrlist alist;
1570 struct FinderAttrBuf volFinderInfo;
1571 VolumeUUID *finderInfoUUIDPtr;
1572 int result;
1573
1574 /* Set up the attrlist structure to get the volume's Finder Info */
1575 alist.bitmapcount = 5;
1576 alist.reserved = 0;
1577 alist.commonattr = ATTR_CMN_FNDRINFO;
1578 alist.volattr = ATTR_VOL_INFO;
1579 alist.dirattr = 0;
1580 alist.fileattr = 0;
1581 alist.forkattr = 0;
1582
1583 /* Get the Finder Info */
1584 result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1585 if (result) {
1586 result = FSUR_IO_FAIL;
1587 goto Err_Exit;
1588 }
1589
1590 /* Update the UUID in the Finder Info */
1591 finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1592 finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1593 finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1594
1595 /* Write the Finder Info back to the volume */
1596 result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
1597 if (result) {
1598 result = FSUR_IO_FAIL;
1599 goto Err_Exit;
1600 }
1601
1602 result = FSUR_IO_SUCCESS;
1603
1604 Err_Exit:
1605 return result;
1606 }
1607
1608
1609 /*
1610 GetVolumeUUID
1611
1612 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1613 we were asked to generate one, then generate a new UUID and write it to the
1614 volume.
1615
1616 Determine whether an HFS volume is mounted on the given device. If so, we
1617 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1618 the filesystem. If there is no mounted volume, then do direct device access
1619 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1620
1621 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1622 */
1623
1624 static int
1625 GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate)
1626 {
1627 int result;
1628 char *path = NULL;
1629
1630 /*
1631 * Determine whether a volume is mounted on this device. If it is HFS, then
1632 * get the mount point's path. If it is non-HFS, then we can exit immediately
1633 * with FSUR_UNRECOGNIZED.
1634 */
1635 result = GetHFSMountPoint(deviceNamePtr, &path);
1636 if (result != FSUR_IO_SUCCESS)
1637 goto Err_Exit;
1638
1639 /*
1640 * Get any existing UUID.
1641 */
1642 if (path)
1643 result = GetVolumeUUIDAttr(path, volumeUUIDPtr);
1644 else
1645 result = GetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1646 if (result != FSUR_IO_SUCCESS)
1647 goto Err_Exit;
1648
1649 /*
1650 * If there was no valid UUID, and we were asked to generate one, then
1651 * generate it and write it back to disk.
1652 */
1653 if (generate && (volumeUUIDPtr->v.high == 0 || volumeUUIDPtr->v.low == 0)) {
1654 GenerateVolumeUUID(volumeUUIDPtr);
1655 if (path)
1656 result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1657 else
1658 result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1659 /* Fall through to Err_Exit */
1660 }
1661
1662 Err_Exit:
1663 return result;
1664 }
1665
1666
1667
1668 /*
1669 SetVolumeUUID
1670
1671 Write a UUID to an HFS, HFS Plus or HFSX volume.
1672
1673 Determine whether an HFS volume is mounted on the given device. If so, we
1674 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1675 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1676
1677 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1678 */
1679 static int
1680 SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) {
1681 int result;
1682 char *path = NULL;
1683
1684 /*
1685 * Determine whether a volume is mounted on this device. If it is HFS, then
1686 * get the mount point's path. If it is non-HFS, then we can exit immediately
1687 * with FSUR_UNRECOGNIZED.
1688 */
1689 result = GetHFSMountPoint(deviceNamePtr, &path);
1690 if (result != FSUR_IO_SUCCESS)
1691 goto Err_Exit;
1692
1693 /*
1694 * Update the UUID.
1695 */
1696 if (path)
1697 result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1698 else
1699 result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1700
1701 Err_Exit:
1702 return result;
1703 }
1704
1705
1706
1707 /*
1708 -- GetEmbeddedHFSPlusVol
1709 --
1710 -- In: hfsMasterDirectoryBlockPtr
1711 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1712 (that is, 2 blocks before the volume header)
1713 --
1714 */
1715
1716 static int
1717 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr)
1718 {
1719 int result = FSUR_IO_SUCCESS;
1720 u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount;
1721
1722 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drSigWord) != kHFSSigWord) {
1723 result = FSUR_UNRECOGNIZED;
1724 goto Return;
1725 }
1726
1727 allocationBlockSize = OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr->drAlBlkSiz);
1728 firstAllocationBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drAlBlSt);
1729
1730 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedSigWord) != kHFSPlusSigWord) {
1731 result = FSUR_UNRECOGNIZED;
1732 goto Return;
1733 }
1734
1735 startBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock);
1736 blockCount = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount);
1737
1738 if ( startOffsetPtr )
1739 *startOffsetPtr = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) +
1740 ((u_int64_t)firstAllocationBlock * (u_int64_t)HFS_BLOCK_SIZE);
1741
1742 Return:
1743 return result;
1744
1745 }
1746
1747
1748
1749 /*
1750 -- GetNameFromHFSPlusVolumeStartingAt
1751 --
1752 -- Caller's responsibility to allocate and release memory for the converted string.
1753 --
1754 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1755 */
1756
1757 static int
1758 GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o)
1759 {
1760 int result = FSUR_IO_SUCCESS;
1761 u_int32_t blockSize;
1762 char * bufPtr = NULL;
1763 HFSPlusVolumeHeader * volHdrPtr;
1764 BTNodeDescriptor * bTreeNodeDescriptorPtr;
1765 u_int32_t catalogNodeSize;
1766 u_int32_t leafNode;
1767 u_int32_t catalogExtCount;
1768 HFSPlusExtentDescriptor *catalogExtents = NULL;
1769
1770 volHdrPtr = (HFSPlusVolumeHeader *)malloc(HFS_BLOCK_SIZE);
1771 if ( ! volHdrPtr ) {
1772 result = FSUR_IO_FAIL;
1773 goto Return;
1774 }
1775
1776 /*
1777 * Read the Volume Header
1778 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1779 */
1780 result = readAt( fd, volHdrPtr, hfsPlusVolumeOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE );
1781 if (result == FSUR_IO_FAIL) {
1782 #if TRACE_HFS_UTIL
1783 fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1784 #endif
1785 goto Return; // return FSUR_IO_FAIL
1786 }
1787
1788 /* Verify that it is an HFS+ volume. */
1789
1790 if (OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSPlusSigWord &&
1791 OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSXSigWord) {
1792 result = FSUR_IO_FAIL;
1793 #if TRACE_HFS_UTIL
1794 fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1795 #endif
1796 goto Return;
1797 }
1798
1799 blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
1800 catalogExtents = (HFSPlusExtentDescriptor *) malloc(sizeof(HFSPlusExtentRecord));
1801 if ( ! catalogExtents ) {
1802 result = FSUR_IO_FAIL;
1803 goto Return;
1804 }
1805 bcopy(volHdrPtr->catalogFile.extents, catalogExtents, sizeof(HFSPlusExtentRecord));
1806 catalogExtCount = kHFSPlusExtentDensity;
1807
1808 /* if there are overflow catalog extents, then go get them */
1809 if (OSSwapBigToHostInt32(catalogExtents[7].blockCount) != 0) {
1810 result = GetCatalogOverflowExtents(fd, hfsPlusVolumeOffset, volHdrPtr, &catalogExtents, &catalogExtCount);
1811 if (result != FSUR_IO_SUCCESS)
1812 goto Return;
1813 }
1814
1815 /* Read the header node of the catalog B-Tree */
1816
1817 result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
1818 catalogExtCount, catalogExtents,
1819 &catalogNodeSize, &leafNode);
1820 if (result != FSUR_IO_SUCCESS)
1821 goto Return;
1822
1823 /* Read the first leaf node of the catalog b-tree */
1824
1825 bufPtr = (char *)malloc(catalogNodeSize);
1826 if ( ! bufPtr ) {
1827 result = FSUR_IO_FAIL;
1828 goto Return;
1829 }
1830
1831 bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
1832
1833 result = ReadFile(fd, bufPtr, (off_t) leafNode * (off_t) catalogNodeSize, catalogNodeSize,
1834 hfsPlusVolumeOffset, blockSize,
1835 catalogExtCount, catalogExtents);
1836 if (result == FSUR_IO_FAIL) {
1837 #if TRACE_HFS_UTIL
1838 fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
1839 #endif
1840 goto Return; // return FSUR_IO_FAIL
1841 }
1842
1843 {
1844 u_int16_t * v;
1845 char * p;
1846 HFSPlusCatalogKey * k;
1847 CFStringRef cfstr;
1848
1849 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords) < 1) {
1850 result = FSUR_IO_FAIL;
1851 #if TRACE_HFS_UTIL
1852 fprintf(stderr, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1853 #endif
1854 goto Return;
1855 }
1856
1857 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1858
1859 p = bufPtr + catalogNodeSize - sizeof(u_int16_t); // pointer arithmetic in bytes
1860 v = (u_int16_t *)p;
1861
1862 // Get a pointer to the first record.
1863
1864 p = bufPtr + OSSwapBigToHostInt16(*v); // pointer arithmetic in bytes
1865 k = (HFSPlusCatalogKey *)p;
1866
1867 // There should be only one record whose parent is the root parent. It should be the first record.
1868
1869 if (OSSwapBigToHostInt32(k->parentID) != kHFSRootParentID) {
1870 result = FSUR_IO_FAIL;
1871 #if TRACE_HFS_UTIL
1872 fprintf(stderr, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1873 #endif
1874 goto Return;
1875 }
1876
1877 /* Extract the name of the root directory */
1878
1879 {
1880 HFSUniStr255 *swapped;
1881 int i;
1882
1883 swapped = (HFSUniStr255 *)malloc(sizeof(HFSUniStr255));
1884 if (swapped == NULL) {
1885 result = FSUR_IO_FAIL;
1886 goto Return;
1887 }
1888 swapped->length = OSSwapBigToHostInt16(k->nodeName.length);
1889
1890 for (i=0; i<swapped->length; i++) {
1891 swapped->unicode[i] = OSSwapBigToHostInt16(k->nodeName.unicode[i]);
1892 }
1893 swapped->unicode[i] = 0;
1894 cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, swapped->unicode, swapped->length);
1895 (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX, kCFStringEncodingUTF8);
1896 CFRelease(cfstr);
1897 free(swapped);
1898 }
1899 }
1900
1901 result = FSUR_IO_SUCCESS;
1902
1903 Return:
1904 if (volHdrPtr)
1905 free((char*) volHdrPtr);
1906
1907 if (catalogExtents)
1908 free((char*) catalogExtents);
1909
1910 if (bufPtr)
1911 free((char*)bufPtr);
1912
1913 return result;
1914
1915 } /* GetNameFromHFSPlusVolumeStartingAt */
1916
1917
1918 #pragma options align=mac68k
1919 typedef struct {
1920 BTNodeDescriptor node;
1921 BTHeaderRec header;
1922 } HeaderRec, *HeaderPtr;
1923 #pragma options align=reset
1924
1925 /*
1926 --
1927 --
1928 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1929 --
1930 */
1931 static int
1932 GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize,
1933 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
1934 u_int32_t *nodeSize, u_int32_t *firstLeafNode)
1935 {
1936 int result;
1937 HeaderRec * bTreeHeaderPtr = NULL;
1938
1939 bTreeHeaderPtr = (HeaderRec *) malloc(HFS_BLOCK_SIZE);
1940 if (bTreeHeaderPtr == NULL)
1941 return (FSUR_IO_FAIL);
1942
1943 /* Read the b-tree header node */
1944
1945 result = ReadFile(fd, bTreeHeaderPtr, 0, HFS_BLOCK_SIZE,
1946 hfsPlusVolumeOffset, blockSize,
1947 extentCount, extentList);
1948 if ( result == FSUR_IO_FAIL ) {
1949 #if TRACE_HFS_UTIL
1950 fprintf(stderr, "hfs.util: ERROR: reading header node failed\n");
1951 #endif
1952 goto free;
1953 }
1954
1955 if ( bTreeHeaderPtr->node.kind != kBTHeaderNode ) {
1956 result = FSUR_IO_FAIL;
1957 #if TRACE_HFS_UTIL
1958 fprintf(stderr, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1959 #endif
1960 goto free;
1961 }
1962
1963 *nodeSize = OSSwapBigToHostInt16(bTreeHeaderPtr->header.nodeSize);
1964
1965 if (OSSwapBigToHostInt32(bTreeHeaderPtr->header.leafRecords) == 0)
1966 *firstLeafNode = 0;
1967 else
1968 *firstLeafNode = OSSwapBigToHostInt32(bTreeHeaderPtr->header.firstLeafNode);
1969
1970 free:;
1971 free((char*) bTreeHeaderPtr);
1972
1973 return result;
1974
1975 } /* GetBTreeNodeInfo */
1976
1977
1978 /*
1979 --
1980 --
1981 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1982 --
1983 */
1984 static int
1985 GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset,
1986 HFSPlusVolumeHeader *volHdrPtr,
1987 HFSPlusExtentDescriptor **catalogExtents,
1988 u_int32_t *catalogExtCount)
1989 {
1990 off_t offset;
1991 u_int32_t numRecords;
1992 u_int32_t nodeSize;
1993 u_int32_t leafNode;
1994 u_int32_t blockSize;
1995 BTNodeDescriptor * bTreeNodeDescriptorPtr;
1996 HFSPlusExtentDescriptor * extents;
1997 size_t listsize;
1998 char * bufPtr = NULL;
1999 int i;
2000 int result;
2001
2002 blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
2003 listsize = *catalogExtCount * sizeof(HFSPlusExtentDescriptor);
2004 extents = *catalogExtents;
2005 offset = (off_t)OSSwapBigToHostInt32(volHdrPtr->extentsFile.extents[0].startBlock) *
2006 (off_t)blockSize;
2007
2008 /* Read the header node of the extents B-Tree */
2009
2010 result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
2011 kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents,
2012 &nodeSize, &leafNode);
2013 if (result != FSUR_IO_SUCCESS || leafNode == 0)
2014 goto Return;
2015
2016 /* Calculate the logical position of the first leaf node */
2017
2018 offset = (off_t) leafNode * (off_t) nodeSize;
2019
2020 /* Read the first leaf node of the extents b-tree */
2021
2022 bufPtr = (char *)malloc(nodeSize);
2023 if (! bufPtr) {
2024 result = FSUR_IO_FAIL;
2025 goto Return;
2026 }
2027
2028 bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
2029
2030 again:
2031 result = ReadFile(fd, bufPtr, offset, nodeSize,
2032 hfsPlusVolumeOffset, blockSize,
2033 kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents);
2034 if ( result == FSUR_IO_FAIL ) {
2035 #if TRACE_HFS_UTIL
2036 fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
2037 #endif
2038 goto Return;
2039 }
2040
2041 if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) {
2042 result = FSUR_IO_FAIL;
2043 goto Return;
2044 }
2045
2046 numRecords = OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords);
2047 for (i = 1; i <= numRecords; ++i) {
2048 u_int16_t * v;
2049 char * p;
2050 HFSPlusExtentKey * k;
2051
2052 /*
2053 * Get the offset (in bytes) of the record from the
2054 * list of offsets at the end of the node
2055 */
2056 p = bufPtr + nodeSize - (sizeof(u_int16_t) * i);
2057 v = (u_int16_t *)p;
2058
2059 /* Get a pointer to the record */
2060
2061 p = bufPtr + OSSwapBigToHostInt16(*v); /* pointer arithmetic in bytes */
2062 k = (HFSPlusExtentKey *)p;
2063
2064 if (OSSwapBigToHostInt32(k->fileID) != kHFSCatalogFileID)
2065 goto Return;
2066
2067 /* grow list and copy additional extents */
2068 listsize += sizeof(HFSPlusExtentRecord);
2069 extents = (HFSPlusExtentDescriptor *) realloc(extents, listsize);
2070 bcopy(p + OSSwapBigToHostInt16(k->keyLength) + sizeof(u_int16_t),
2071 &extents[*catalogExtCount], sizeof(HFSPlusExtentRecord));
2072
2073 *catalogExtCount += kHFSPlusExtentDensity;
2074 *catalogExtents = extents;
2075 }
2076
2077 if ((leafNode = OSSwapBigToHostInt32(bTreeNodeDescriptorPtr->fLink)) != 0) {
2078
2079 offset = (off_t) leafNode * (off_t) nodeSize;
2080
2081 goto again;
2082 }
2083
2084 Return:;
2085 if (bufPtr)
2086 free(bufPtr);
2087
2088 return (result);
2089 }
2090
2091
2092
2093 /*
2094 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2095 * position and number of contiguous bytes at that position.
2096 *
2097 * Inputs:
2098 * logicalOffset Logical offset in bytes from start of file
2099 * length Maximum number of bytes to map
2100 * blockSize Number of bytes per allocation block
2101 * extentCount Number of extents in file
2102 * extentList The file's extents
2103 *
2104 * Outputs:
2105 * physicalOffset Physical offset in bytes from start of volume
2106 * availableBytes Number of bytes physically contiguous (up to length)
2107 *
2108 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2109 */
2110 static int LogicalToPhysical(off_t offset, ssize_t length, u_int32_t blockSize,
2111 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
2112 off_t *physicalOffset, ssize_t *availableBytes)
2113 {
2114 off_t temp;
2115 u_int32_t logicalBlock;
2116 u_int32_t extent;
2117 u_int32_t blockCount = 0;
2118
2119 /* Determine allocation block containing logicalOffset */
2120 logicalBlock = offset / blockSize; /* This can't overflow for valid volumes */
2121 offset %= blockSize; /* Offset from start of allocation block */
2122
2123 /* Find the extent containing logicalBlock */
2124 for (extent = 0; extent < extentCount; ++extent)
2125 {
2126 blockCount = OSSwapBigToHostInt32(extentList[extent].blockCount);
2127
2128 if (blockCount == 0)
2129 return FSUR_IO_FAIL; /* Tried to map past physical end of file */
2130
2131 if (logicalBlock < blockCount)
2132 break; /* Found it! */
2133
2134 logicalBlock -= blockCount;
2135 }
2136
2137 if (extent >= extentCount)
2138 return FSUR_IO_FAIL; /* Tried to map past physical end of file */
2139
2140 /*
2141 * When we get here, extentList[extent] is the extent containing logicalOffset.
2142 * The desired allocation block is logicalBlock blocks into the extent.
2143 */
2144
2145 /* Compute the physical starting position */
2146 temp = OSSwapBigToHostInt32(extentList[extent].startBlock) + logicalBlock; /* First physical block */
2147 temp *= blockSize; /* Byte offset of first physical block */
2148 *physicalOffset = temp + offset;
2149
2150 /* Compute the available contiguous bytes. */
2151 temp = blockCount - logicalBlock; /* Number of blocks available in extent */
2152 temp *= blockSize;
2153 temp -= offset; /* Number of bytes available */
2154
2155 if (temp < length)
2156 *availableBytes = temp;
2157 else
2158 *availableBytes = length;
2159
2160 return FSUR_IO_SUCCESS;
2161 }
2162
2163
2164
2165 /*
2166 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2167 * ending position are not allocation or device block aligned.
2168 *
2169 * Inputs:
2170 * fd Descriptor for reading the volume
2171 * buffer The bytes are read into here
2172 * offset Offset in file to start reading
2173 * length Number of bytes to read
2174 * volOffset Byte offset from start of device to start of volume
2175 * blockSize Number of bytes per allocation block
2176 * extentCount Number of extents in file
2177 * extentList The file's exents
2178 *
2179 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2180 */
2181 static int ReadFile(int fd, void *buffer, off_t offset, ssize_t length,
2182 off_t volOffset, u_int32_t blockSize,
2183 u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList)
2184 {
2185 int result = FSUR_IO_SUCCESS;
2186 off_t physOffset;
2187 ssize_t physLength;
2188
2189 while (length > 0)
2190 {
2191 result = LogicalToPhysical(offset, length, blockSize, extentCount, extentList,
2192 &physOffset, &physLength);
2193 if (result != FSUR_IO_SUCCESS)
2194 break;
2195
2196 result = readAt(fd, buffer, volOffset+physOffset, physLength);
2197 if (result != FSUR_IO_SUCCESS)
2198 break;
2199
2200 length -= physLength;
2201 offset += physLength;
2202 buffer = (char *) buffer + physLength;
2203 }
2204
2205 return result;
2206 }
2207
2208 /*
2209 -- readAt = lseek() + read()
2210 --
2211 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2212 --
2213 */
2214
2215 static ssize_t
2216 readAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2217 {
2218 int blocksize;
2219 off_t lseekResult;
2220 ssize_t readResult;
2221 void * rawData = NULL;
2222 off_t rawOffset;
2223 ssize_t rawLength;
2224 ssize_t dataOffset = 0;
2225 int result = FSUR_IO_SUCCESS;
2226
2227 if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2228 #if TRACE_HFS_UTIL
2229 fprintf(stderr, "hfs.util: readAt: couldn't determine block size of device.\n");
2230 #endif
2231 result = FSUR_IO_FAIL;
2232 goto Return;
2233 }
2234 /* put offset and length in terms of device blocksize */
2235 rawOffset = offset / blocksize * blocksize;
2236 dataOffset = offset - rawOffset;
2237 rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2238 rawData = malloc(rawLength);
2239 if (rawData == NULL) {
2240 result = FSUR_IO_FAIL;
2241 goto Return;
2242 }
2243
2244 lseekResult = lseek( fd, rawOffset, SEEK_SET );
2245 if ( lseekResult != rawOffset ) {
2246 result = FSUR_IO_FAIL;
2247 goto Return;
2248 }
2249
2250 readResult = read(fd, rawData, rawLength);
2251 if ( readResult != rawLength ) {
2252 #if TRACE_HFS_UTIL
2253 fprintf(stderr, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno);
2254 #endif
2255 result = FSUR_IO_FAIL;
2256 goto Return;
2257 }
2258 bcopy(rawData + dataOffset, bufPtr, length);
2259
2260 Return:
2261 if (rawData) {
2262 free(rawData);
2263 }
2264 return result;
2265
2266 } /* readAt */
2267
2268 /*
2269 -- writeAt = lseek() + write()
2270 --
2271 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2272 --
2273 */
2274
2275 static ssize_t
2276 writeAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2277 {
2278 int blocksize;
2279 off_t deviceoffset;
2280 ssize_t bytestransferred;
2281 void * rawData = NULL;
2282 off_t rawOffset;
2283 ssize_t rawLength;
2284 ssize_t dataOffset = 0;
2285 int result = FSUR_IO_SUCCESS;
2286
2287 if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2288 #if TRACE_HFS_UTIL
2289 fprintf(stderr, "hfs.util: couldn't determine block size of device.\n");
2290 #endif
2291 result = FSUR_IO_FAIL;
2292 goto Return;
2293 }
2294 /* put offset and length in terms of device blocksize */
2295 rawOffset = offset / blocksize * blocksize;
2296 dataOffset = offset - rawOffset;
2297 rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2298 rawData = malloc(rawLength);
2299 if (rawData == NULL) {
2300 result = FSUR_IO_FAIL;
2301 goto Return;
2302 }
2303
2304 deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2305 if ( deviceoffset != rawOffset ) {
2306 result = FSUR_IO_FAIL;
2307 goto Return;
2308 }
2309
2310 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2311 if (((rawOffset % blocksize) != 0) || ((rawLength % blocksize) != 0)) {
2312 bytestransferred = read(fd, rawData, rawLength);
2313 if ( bytestransferred != rawLength ) {
2314 #if TRACE_HFS_UTIL
2315 fprintf(stderr, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno);
2316 #endif
2317 result = FSUR_IO_FAIL;
2318 goto Return;
2319 }
2320 }
2321
2322 bcopy(bufPtr, rawData + dataOffset, length); /* Copy in the new data */
2323
2324 deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2325 if ( deviceoffset != rawOffset ) {
2326 result = FSUR_IO_FAIL;
2327 goto Return;
2328 }
2329
2330 bytestransferred = write(fd, rawData, rawLength);
2331 if ( bytestransferred != rawLength ) {
2332 #if TRACE_HFS_UTIL
2333 fprintf(stderr, "writeAt: attempt to write data to device failed?!");
2334 #endif
2335 result = FSUR_IO_FAIL;
2336 goto Return;
2337 }
2338
2339 Return:
2340 if (rawData) free(rawData);
2341
2342 return result;
2343
2344 } /* writeAt */
2345
2346
2347 /*
2348 * Get kernel's encoding bias.
2349 */
2350 static int
2351 GetEncodingBias()
2352 {
2353 int mib[3];
2354 size_t buflen = sizeof(int);
2355 struct vfsconf vfc;
2356 int hint = 0;
2357
2358 if (getvfsbyname("hfs", &vfc) < 0)
2359 goto error;
2360
2361 mib[0] = CTL_VFS;
2362 mib[1] = vfc.vfc_typenum;
2363 mib[2] = HFS_ENCODINGBIAS;
2364
2365 if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
2366 goto error;
2367 return (hint);
2368 error:
2369 return (-1);
2370 }
2371
2372 /******************************************************************************
2373 *
2374 * 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
2375 *
2376 *****************************************************************************/
2377
2378 #define DBHANDLESIGNATURE 0x75917737
2379
2380 /* Flag values for operation options: */
2381 #define DBMARKPOSITION 1
2382
2383 static char gVSDBPath[] = "/var/db/volinfo.database";
2384
2385 #define MAXIOMALLOC 16384
2386
2387 /* Database layout: */
2388
2389 struct VSDBKey {
2390 char uuid[16];
2391 };
2392
2393 struct VSDBRecord {
2394 char statusFlags[8];
2395 };
2396
2397 struct VSDBEntry {
2398 struct VSDBKey key;
2399 char keySeparator;
2400 char space;
2401 struct VSDBRecord record;
2402 char terminator;
2403 };
2404
2405 #define DBKEYSEPARATOR ':'
2406 #define DBBLANKSPACE ' '
2407 #define DBRECORDTERMINATOR '\n'
2408
2409 /* In-memory data structures: */
2410
2411 struct VSDBState {
2412 unsigned long signature;
2413 int dbfile;
2414 int dbmode;
2415 off_t recordPosition;
2416 };
2417
2418 typedef struct VSDBState *VSDBStatePtr;
2419
2420
2421
2422 /* Internal function prototypes: */
2423 static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
2424 static int UnlockDB(VSDBStatePtr dbstateptr);
2425
2426 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, unsigned long options);
2427 static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2428 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2429 static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2430 static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2);
2431
2432 static void FormatULong(unsigned long u, char *s);
2433 static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
2434 static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
2435 static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord);
2436 static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry);
2437 static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits);
2438
2439
2440
2441 /******************************************************************************
2442 *
2443 * 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
2444 *
2445 *****************************************************************************/
2446
2447 void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
2448 SHA_CTX context;
2449 char randomInputBuffer[26];
2450 unsigned char digest[20];
2451 time_t now;
2452 clock_t uptime;
2453 int mib[2];
2454 int sysdata;
2455 char sysctlstring[128];
2456 size_t datalen;
2457 double sysloadavg[3];
2458 struct vmtotal sysvmtotal;
2459
2460 do {
2461 /* Initialize the SHA-1 context for processing: */
2462 SHA1_Init(&context);
2463
2464 /* Now process successive bits of "random" input to seed the process: */
2465
2466 /* The current system's uptime: */
2467 uptime = clock();
2468 SHA1_Update(&context, &uptime, sizeof(uptime));
2469
2470 /* The kernel's boot time: */
2471 mib[0] = CTL_KERN;
2472 mib[1] = KERN_BOOTTIME;
2473 datalen = sizeof(sysdata);
2474 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2475 SHA1_Update(&context, &sysdata, datalen);
2476
2477 /* The system's host id: */
2478 mib[0] = CTL_KERN;
2479 mib[1] = KERN_HOSTID;
2480 datalen = sizeof(sysdata);
2481 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2482 SHA1_Update(&context, &sysdata, datalen);
2483
2484 /* The system's host name: */
2485 mib[0] = CTL_KERN;
2486 mib[1] = KERN_HOSTNAME;
2487 datalen = sizeof(sysctlstring);
2488 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2489 SHA1_Update(&context, sysctlstring, datalen);
2490
2491 /* The running kernel's OS release string: */
2492 mib[0] = CTL_KERN;
2493 mib[1] = KERN_OSRELEASE;
2494 datalen = sizeof(sysctlstring);
2495 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2496 SHA1_Update(&context, sysctlstring, datalen);
2497
2498 /* The running kernel's version string: */
2499 mib[0] = CTL_KERN;
2500 mib[1] = KERN_VERSION;
2501 datalen = sizeof(sysctlstring);
2502 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2503 SHA1_Update(&context, sysctlstring, datalen);
2504
2505 /* The system's load average: */
2506 datalen = sizeof(sysloadavg);
2507 getloadavg(sysloadavg, 3);
2508 SHA1_Update(&context, &sysloadavg, datalen);
2509
2510 /* The system's VM statistics: */
2511 mib[0] = CTL_VM;
2512 mib[1] = VM_METER;
2513 datalen = sizeof(sysvmtotal);
2514 sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
2515 SHA1_Update(&context, &sysvmtotal, datalen);
2516
2517 /* The current GMT (26 ASCII characters): */
2518 time(&now);
2519 strncpy(randomInputBuffer, asctime(gmtime(&now)), 26); /* "Mon Mar 27 13:46:26 2000" */
2520 SHA1_Update(&context, randomInputBuffer, 26);
2521
2522 /* Pad the accumulated input and extract the final digest hash: */
2523 SHA1_Final(digest, &context);
2524
2525 memcpy(newVolumeID, digest, sizeof(*newVolumeID));
2526 } while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
2527 }
2528
2529
2530
2531 void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) {
2532 int i;
2533 char c;
2534 unsigned long nextdigit;
2535 unsigned long high = 0;
2536 unsigned long low = 0;
2537 unsigned long carry;
2538
2539 for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) {
2540 if ((c >= '0') && (c <= '9')) {
2541 nextdigit = c - '0';
2542 } else if ((c >= 'A') && (c <= 'F')) {
2543 nextdigit = c - 'A' + 10;
2544 } else if ((c >= 'a') && (c <= 'f')) {
2545 nextdigit = c - 'a' + 10;
2546 } else {
2547 nextdigit = 0;
2548 }
2549 carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
2550 high = (high << 4) | carry;
2551 low = (low << 4) | nextdigit;
2552 }
2553
2554 volumeID->v.high = high;
2555 volumeID->v.low = low;
2556 }
2557
2558
2559
2560 void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
2561 FormatUUID(volumeID, UUIDString);
2562 *(UUIDString+16) = (char)0; /* Append a terminating null character */
2563 }
2564
2565
2566
2567 int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
2568 VSDBStatePtr dbstateptr;
2569
2570 *DBHandlePtr = NULL;
2571
2572 dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
2573 if (dbstateptr == NULL) {
2574 return ENOMEM;
2575 }
2576
2577 dbstateptr->dbmode = O_RDWR;
2578 dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2579 if (dbstateptr->dbfile == -1) {
2580 /*
2581 The file couldn't be opened for read/write access:
2582 try read-only access before giving up altogether.
2583 */
2584 dbstateptr->dbmode = O_RDONLY;
2585 dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2586 if (dbstateptr->dbfile == -1) {
2587 return errno;
2588 }
2589 }
2590
2591 dbstateptr->signature = DBHANDLESIGNATURE;
2592 *DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
2593 return 0;
2594 }
2595
2596
2597
2598 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) {
2599 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2600 struct VSDBEntry dbentry;
2601 int result;
2602
2603 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2604
2605 if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
2606
2607 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
2608 goto ErrExit;
2609 }
2610 *VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
2611
2612 result = 0;
2613
2614 ErrExit:
2615 UnlockDB(dbstateptr);
2616 return result;
2617 }
2618
2619
2620
2621 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) {
2622 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2623 struct VSDBEntry dbentry;
2624 int result;
2625
2626 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2627 if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
2628
2629 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2630
2631 FormatDBEntry(volumeID, VolumeStatus, &dbentry);
2632 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
2633 #if DEBUG_TRACE
2634 fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2635 #endif
2636 result = UpdateVolumeRecord(dbstateptr, &dbentry);
2637 } else if (result == -1) {
2638 #if DEBUG_TRACE
2639 fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2640 #endif
2641 result = AddVolumeRecord(dbstateptr, &dbentry);
2642 } else {
2643 goto ErrExit;
2644 }
2645
2646 fsync(dbstateptr->dbfile);
2647
2648 result = 0;
2649
2650 ErrExit:
2651 UnlockDB(dbstateptr);
2652 return result;
2653 }
2654
2655
2656
2657 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
2658 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2659 struct stat dbinfo;
2660 int result;
2661 unsigned long iobuffersize;
2662 void *iobuffer = NULL;
2663 off_t dataoffset;
2664 unsigned long iotransfersize;
2665 unsigned long bytestransferred;
2666
2667 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2668
2669 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2670
2671 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
2672 #if DEBUG_TRACE
2673 fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
2674 #endif
2675 if (result == -1) result = 0; /* Entry wasn't in the database to begin with? */
2676 goto StdEdit;
2677 } else {
2678 #if DEBUG_TRACE
2679 fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2680 #endif
2681 if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
2682 if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) {
2683 iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry);
2684 } else {
2685 iobuffersize = MAXIOMALLOC;
2686 }
2687 #if DEBUG_TRACE
2688 fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2689 (unsigned long)dbinfo.st_size, (unsigned long)dbstateptr->recordPosition);
2690 fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
2691 #endif
2692 if (iobuffersize > 0) {
2693 iobuffer = malloc(iobuffersize);
2694 if (iobuffer == NULL) {
2695 result = ENOMEM;
2696 goto ErrExit;
2697 }
2698
2699 dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
2700 do {
2701 iotransfersize = dbinfo.st_size - dataoffset;
2702 if (iotransfersize > 0) {
2703 if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
2704
2705 #if DEBUG_TRACE
2706 fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)dataoffset);
2707 #endif
2708 lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
2709 bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
2710 if (bytestransferred != iotransfersize) {
2711 result = errno;
2712 goto ErrExit;
2713 }
2714
2715 #if DEBUG_TRACE
2716 fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntry)));
2717 #endif
2718 lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
2719 bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
2720 if (bytestransferred != iotransfersize) {
2721 result = errno;
2722 goto ErrExit;
2723 }
2724
2725 dataoffset += (off_t)iotransfersize;
2726 }
2727 } while (iotransfersize > 0);
2728 }
2729 #if DEBUG_TRACE
2730 fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry))));
2731 #endif
2732 if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
2733 goto ErrExit;
2734 }
2735
2736 fsync(dbstateptr->dbfile);
2737
2738 result = 0;
2739 }
2740
2741 ErrExit:
2742 if (iobuffer) free(iobuffer);
2743 UnlockDB(dbstateptr);
2744
2745 StdEdit:
2746 return result;
2747 }
2748
2749
2750
2751 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
2752 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2753
2754 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2755
2756 dbstateptr->signature = 0;
2757
2758 close(dbstateptr->dbfile); /* Nothing we can do about any errors... */
2759 dbstateptr->dbfile = 0;
2760
2761 free(dbstateptr);
2762
2763 return 0;
2764 }
2765
2766
2767
2768 /******************************************************************************
2769 *
2770 * 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
2771 *
2772 *****************************************************************************/
2773
2774 static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
2775 #if DEBUG_TRACE
2776 fprintf(stderr, "LockDB: Locking VSDB file...\n");
2777 #endif
2778 return flock(dbstateptr->dbfile, lockmode);
2779 }
2780
2781
2782
2783 static int UnlockDB(VSDBStatePtr dbstateptr) {
2784 #if DEBUG_TRACE
2785 fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
2786 #endif
2787 return flock(dbstateptr->dbfile, LOCK_UN);
2788 }
2789
2790
2791
2792 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) {
2793 struct VSDBKey searchkey;
2794 struct VSDBEntry dbentry;
2795 int result;
2796
2797 FormatDBKey(volumeID, &searchkey);
2798 lseek(dbstateptr->dbfile, 0, SEEK_SET);
2799
2800 do {
2801 result = GetVSDBEntry(dbstateptr, &dbentry);
2802 if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
2803 if (targetEntry != NULL) {
2804 #if DEBUG_TRACE
2805 fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry);
2806 #endif
2807 memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
2808 }
2809 return 0;
2810 }
2811 } while (result == 0);
2812
2813 return -1;
2814 }
2815
2816
2817
2818 static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) {
2819 #if DEBUG_TRACE
2820 VolumeUUIDString id;
2821 #endif
2822
2823 #if DEBUG_TRACE
2824 strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2825 id[sizeof(dbentry->key.uuid)] = (char)0;
2826 fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id);
2827 #endif
2828 lseek(dbstateptr->dbfile, 0, SEEK_END);
2829 return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
2830 }
2831
2832
2833
2834
2835 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2836 #if DEBUG_TRACE
2837 VolumeUUIDString id;
2838 #endif
2839
2840 #if DEBUG_TRACE
2841 strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2842 id[sizeof(dbentry->key.uuid)] = (char)0;
2843 fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (unsigned long)dbstateptr->recordPosition);
2844 #endif
2845 lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET);
2846 #if DEBUG_TRACE
2847 fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry));
2848 #endif
2849 return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry));
2850 }
2851
2852
2853
2854 static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2855 struct VSDBEntry entry;
2856 int result;
2857 #if DEBUG_TRACE
2858 VolumeUUIDString id;
2859 #endif
2860
2861 dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR);
2862 #if 0 // DEBUG_TRACE
2863 fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr->recordPosition);
2864 #endif
2865 result = read(dbstateptr->dbfile, &entry, sizeof(entry));
2866 if ((result != sizeof(entry)) ||
2867 (entry.keySeparator != DBKEYSEPARATOR) ||
2868 (entry.space != DBBLANKSPACE) ||
2869 (entry.terminator != DBRECORDTERMINATOR)) {
2870 return -1;
2871 }
2872
2873 #if DEBUG_TRACE
2874 strncpy(id, entry.key.uuid, sizeof(entry.key.uuid));
2875 id[sizeof(entry.key.uuid)] = (char)0;
2876 fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id);
2877 #endif
2878 memcpy(dbentry, &entry, sizeof(*dbentry));
2879 return 0;
2880 }
2881
2882
2883
2884 static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
2885 #if 0 // DEBUG_TRACE
2886 VolumeUUIDString id;
2887
2888 strncpy(id, key1->uuid, sizeof(key1->uuid));
2889 id[sizeof(key1->uuid)] = (char)0;
2890 fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id);
2891 strncpy(id, key2->uuid, sizeof(key2->uuid));
2892 id[sizeof(key2->uuid)] = (char)0;
2893 fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid));
2894 #endif
2895
2896 return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
2897 }
2898
2899
2900
2901 /******************************************************************************
2902 *
2903 * 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
2904 *
2905 *****************************************************************************/
2906
2907 static void FormatULong(unsigned long u, char *s) {
2908 unsigned long d;
2909 int i;
2910 char *digitptr = s;
2911
2912 for (i = 0; i < 8; ++i) {
2913 d = ((u & 0xF0000000) >> 28) & 0x0000000F;
2914 if (d < 10) {
2915 *digitptr++ = (char)(d + '0');
2916 } else {
2917 *digitptr++ = (char)(d - 10 + 'A');
2918 }
2919 u = u << 4;
2920 }
2921 }
2922
2923
2924
2925 static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) {
2926 FormatULong(volumeID->v.high, UUIDField);
2927 FormatULong(volumeID->v.low, UUIDField+8);
2928
2929 }
2930
2931
2932
2933 static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
2934 FormatUUID(volumeID, dbkey->uuid);
2935 }
2936
2937
2938
2939 static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord) {
2940 FormatULong(volumeStatusFlags, dbrecord->statusFlags);
2941 }
2942
2943
2944
2945 static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry) {
2946 FormatDBKey(volumeID, &dbentry->key);
2947 dbentry->keySeparator = DBKEYSEPARATOR;
2948 dbentry->space = DBBLANKSPACE;
2949 FormatDBRecord(volumeStatusFlags, &dbentry->record);
2950 #if 0 // DEBUG_TRACE
2951 dbentry->terminator = (char)0;
2952 fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry));
2953 #endif
2954 dbentry->terminator = DBRECORDTERMINATOR;
2955 }
2956
2957
2958
2959 static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) {
2960 int i;
2961 char c;
2962 unsigned long nextdigit;
2963 unsigned long n;
2964
2965 n = 0;
2966 for (i = 0; (i < 8) && ((c = hs[i]) != (char)0) ; ++i) {
2967 if ((c >= '0') && (c <= '9')) {
2968 nextdigit = c - '0';
2969 } else if ((c >= 'A') && (c <= 'F')) {
2970 nextdigit = c - 'A' + 10;
2971 } else if ((c >= 'a') && (c <= 'f')) {
2972 nextdigit = c - 'a' + 10;
2973 } else {
2974 nextdigit = 0;
2975 }
2976 n = (n << 4) + nextdigit;
2977 }
2978
2979 return n;
2980 }