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