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