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