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