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