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