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