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