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