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