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