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