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