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