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