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