]>
git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/util.c
10 #include <sys/sysctl.h>
11 #include <hfs/hfs_mount.h>
17 * Open the source device. In addition to opening the device,
18 * this also attempts to flush the journal, and then sets up a
19 * DeviceInfo_t object that will be used when doing the actual
24 OpenDevice ( const char * devname
, int flushJournal
)
26 DeviceInfo_t
* retval
= NULL
;
28 DeviceInfo_t dev
= { 0 };
32 if ( stat ( devname
, & sb
) == - 1 ) {
33 err ( kBadExit
, "cannot open device %s " , devname
);
36 * Attempt to flush the journal if requested. If it fails, we just warn, but don't abort.
38 if ( flushJournal
&& getvfsbyname ( "hfs" , & vfc
) == 0 ) {
41 char block_device
[ MAXPATHLEN
+ 1 ];
45 * The journal replay code, sadly, requires a block device.
46 * So we need to go from the raw device to block device, if
49 if ( strncmp ( devname
, "/dev/rdisk" , 10 ) == 0 ) {
50 snprintf ( block_device
, sizeof ( block_device
), "/dev/ %s " , devname
+ 6 );
52 snprintf ( block_device
, sizeof ( block_device
), " %s " , devname
);
54 jfd
= open ( block_device
, O_RDWR
);
56 warn ( "Cannot open block device %s for read-write" , block_device
);
59 mib
[ 1 ] = vfc
. vfc_typenum
;
60 mib
[ 2 ] = HFS_REPLAY_JOURNAL
;
63 fprintf ( stderr
, "about to replay journal \n " );
64 rv
= sysctl ( mib
, 4 , NULL
, NULL
, NULL
, 0 );
66 warn ( "cannot replay journal" );
68 /* This is probably not necessary, but we couldn't prove it. */
69 ( void ) fcntl ( jfd
, F_FULLFSYNC
, 0 );
74 * We only allow a character device (e.g., /dev/rdisk1s2)
75 * If we're given a non-character device, we'll try to turn
76 * into a character device assuming a name pattern of /dev/rdisk*
78 if (( sb
. st_mode
& S_IFMT
) == S_IFCHR
) {
79 dev
. devname
= strdup ( devname
);
80 } else if ( strncmp ( devname
, "/dev/disk" , 9 ) == 0 ) {
81 // Turn "/dev/diskFoo" into "/dev/rdiskFoo"
82 char tmpname
[ strlen ( devname
) + 2 ];
83 ( void ) snprintf ( tmpname
, sizeof ( tmpname
), "/dev/rdisk %s " , devname
+ sizeof ( "/dev/disk" ) - 1 );
84 if ( stat ( tmpname
, & sb
) == - 1 ) {
85 err ( kBadExit
, "cannot open raw device %s " , tmpname
);
87 if (( sb
. st_mode
& S_IFMT
) != S_IFCHR
) {
88 errx ( kBadExit
, "raw device %s is not a raw device" , tmpname
);
90 dev
. devname
= strdup ( tmpname
);
92 errx ( kBadExit
, "device name ` %s ' does not fit pattern" , devname
);
94 // Open with a shared lock if we're not debugging, since the hfs estimator api is invoked from the booted system
95 fd
= open ( dev
. devname
, O_RDONLY
| ( debug
? 0 : O_SHLOCK
));
97 err ( kBadExit
, "cannot open raw device %s " , dev
. devname
);
99 // Get the block size and counts for the device.
100 if ( ioctl ( fd
, DKIOCGETBLOCKSIZE
, & dev
. blockSize
) == - 1 ) {
101 dev
. blockSize
= 512 ; // Sane default, I hope
103 if ( ioctl ( fd
, DKIOCGETBLOCKCOUNT
, & dev
. blockCount
) == - 1 ) {
104 err ( kBadExit
, "cannot get size of device %s " , dev
. devname
);
107 dev
. size
= dev
. blockCount
* dev
. blockSize
;
110 retval
= malloc ( sizeof (* retval
));
111 if ( retval
== NULL
) {
112 err ( kBadExit
, "cannot allocate device info structure" );
119 * Get the header and alternate header for a device.
123 VolumeInfo ( DeviceInfo_t
* devp
)
125 uint8_t buffer
[ devp
-> blockSize
];
126 VolumeDescriptor_t
* vdp
= NULL
, vd
= { 0 };
129 vd
. priOffset
= 1024 ; // primary volume header is at 1024 bytes
130 vd
. altOffset
= devp
-> size
- 1024 ; // alternate header is 1024 bytes from the end
132 rv
= GetBlock ( devp
, vd
. priOffset
, buffer
);
134 err ( kBadExit
, "cannot get primary volume header for device %s " , devp
-> devname
);
136 vd
. priHeader
= *( HFSPlusVolumeHeader
*) buffer
;
138 rv
= GetBlock ( devp
, vd
. altOffset
, buffer
);
140 err ( kBadExit
, "cannot get alternate volume header for device %s " , devp
-> devname
);
142 vd
. altHeader
= *( HFSPlusVolumeHeader
*) buffer
;
144 vdp
= malloc ( sizeof (* vdp
));
151 * Compare the primary and alternate volume headers.
152 * We only care about the "important" bits (namely, the
153 * portions related to extents).
157 CompareVolumeHeaders ( VolumeDescriptor_t
* vdp
)
161 #define CMP_FILE(v, f) memcmp(&(v)->priHeader.f, &(v)->altHeader.f, sizeof(v->priHeader.f))
164 vdp
-> priHeader
. journalInfoBlock
== vdp
-> altHeader
. journalInfoBlock
&&
165 CMP_FILE ( vdp
, allocationFile
) == 0 &&
166 CMP_FILE ( vdp
, extentsFile
) == 0 &&
167 CMP_FILE ( vdp
, catalogFile
) == 0 &&
168 CMP_FILE ( vdp
, attributesFile
) == 0 &&
169 CMP_FILE ( vdp
, startupFile
) == 0 )
176 * Only two (currently) types of signatures are valid: H+ and HX.
179 IsValidSigWord ( uint16_t word
) {
180 if ( word
== kHFSPlusSigWord
||
181 word
== kHFSXSigWord
)
187 * Add the volume headers to the in-core volume information list.
191 AddHeaders ( VolumeObjects_t
* vop
, int roundBlock
)
194 HFSPlusVolumeHeader
* hp
;
195 uint8_t buffer
[ vop
-> devp
-> blockSize
];
198 hp
= & vop
-> vdp
-> priHeader
;
200 if ( IsValidSigWord ( S16 ( hp
-> signature
)) == 0 ) {
201 warnx ( "primary volume header signature = %x , invalid" , S16 ( hp
-> signature
));
205 AddExtent ( vop
, 1024 / vop
-> devp
-> blockSize
, vop
-> devp
-> blockSize
);
207 AddExtent ( vop
, 1024 , 512 );
210 hp
= & vop
-> vdp
-> altHeader
;
212 if ( IsValidSigWord ( S16 ( hp
-> signature
)) == 0 ) {
213 warnx ( "alternate volume header signature = %x , invalid" , S16 ( hp
-> signature
));
217 AddExtent ( vop
, ( vop
-> vdp
-> altOffset
/ vop
-> devp
-> blockSize
) * vop
-> devp
-> blockSize
, vop
-> devp
-> blockSize
);
219 AddExtent ( vop
, vop
-> vdp
-> altOffset
, 512 );
227 * Add the journal information to the in-core volume list.
228 * This means the journal info block, the journal itself, and
229 * the contents of the same as described by the alternate volume
230 * header (if it's different from the primary volume header).
234 AddJournal ( VolumeObjects_t
* vop
)
236 DeviceInfo_t
* devp
= vop
-> devp
;
237 uint8_t buffer
[ devp
-> blockSize
];
239 HFSPlusVolumeHeader
* php
, * ahp
;
240 JournalInfoBlock
* jib
;
242 php
= & vop
-> vdp
-> priHeader
;
243 ahp
= & vop
-> vdp
-> altHeader
;
245 if ( php
-> journalInfoBlock
) {
246 off_t jOffset
= ( off_t
) S32 ( php
-> journalInfoBlock
) * S32 ( php
-> blockSize
);
247 rv
= GetBlock ( devp
, jOffset
, buffer
);
249 err ( kBadExit
, "cannot get primary header's copy of journal info block" );
251 AddExtent ( vop
, jOffset
, sizeof ( buffer
));
252 jib
= ( JournalInfoBlock
*) buffer
;
253 if ( S32 ( jib
-> flags
) & kJIJournalInFSMask
) {
254 AddExtent ( vop
, S64 ( jib
-> offset
), S64 ( jib
-> size
));
258 if ( ahp
-> journalInfoBlock
&&
259 ahp
-> journalInfoBlock
!= php
-> journalInfoBlock
) {
260 off_t jOffset
= ( off_t
) S32 ( ahp
-> journalInfoBlock
) * S32 ( ahp
-> blockSize
);
261 rv
= GetBlock ( devp
, jOffset
, buffer
);
263 err ( kBadExit
, "cannot get alternate header's copy of journal info block" );
265 AddExtent ( vop
, jOffset
, sizeof ( buffer
));
266 jib
= ( JournalInfoBlock
*) buffer
;
267 if ( S32 ( jib
-> flags
) & kJIJournalInFSMask
) {
268 AddExtent ( vop
, S64 ( jib
-> offset
), S64 ( jib
-> size
));
275 * Add the extents for the special files in the volume header. Compare
276 * them with the alternate volume header's versions, and if they're different,
281 AddFileExtents ( VolumeObjects_t
* vop
)
284 #define ADDEXTS(vop, file, fid) \
286 off_t pSize = S32(vop->vdp->priHeader.blockSize); \
287 off_t aSize = S32(vop->vdp->altHeader.blockSize); \
289 if (debug) printf( "Adding " #file " extents \n " ); \
290 for (i = 0; i < kHFSPlusExtentDensity; i++) { \
291 HFSPlusExtentDescriptor *ep = &vop->vdp->priHeader. file .extents[i]; \
292 HFSPlusExtentDescriptor *ap = &vop->vdp->altHeader. file .extents[i]; \
293 if (debug) printf( " \t Extent < %u , %u > \n " , S32(ep->startBlock), S32(ep->blockCount)); \
294 if (ep->startBlock && ep->blockCount) { \
295 AddExtentForFile(vop, S32(ep->startBlock) * pSize, S32(ep->blockCount) * pSize, fid); \
296 if (memcmp(ep, ap, sizeof(*ep)) != 0) { \
297 AddExtentForFile(vop, S32(ap->startBlock) * aSize, S32(ap->blockCount) * aSize, fid); \
304 ADDEXTS ( vop
, allocationFile
, kHFSAllocationFileID
);
305 ADDEXTS ( vop
, extentsFile
, kHFSExtentsFileID
);
306 ADDEXTS ( vop
, catalogFile
, kHFSCatalogFileID
);
307 ADDEXTS ( vop
, attributesFile
, kHFSAttributesFileID
);
308 ADDEXTS ( vop
, startupFile
, kHFSStartupFileID
);
314 ScanExtents ( vop
, useAlt
);
320 ScanCatalogNode ( VolumeObjects_t
* vop
, uint8_t * buffer
, size_t nodeSize
, extent_handler_t handler
)
322 BTNodeDescriptor
* ndp
= ( BTNodeDescriptor
*) buffer
;
323 uint16_t * indices
= ( uint16_t *)( buffer
+ nodeSize
);
325 off_t blockSize
= S32 ( vop
-> vdp
-> priHeader
. blockSize
);
328 if ( ndp
-> kind
!= kBTLeafNode
) // Skip if it's not a leaf node
332 fprintf ( stderr
, " %s : scanning catalog node \n " , __FUNCTION__
);
334 for ( counter
= 1 ; counter
<= S16 ( ndp
-> numRecords
); counter
++) {
335 // Need to get past the end of the key
336 uint16_t recOffset
= S16 ( indices
[- counter
]);
337 HFSPlusCatalogKey
* keyp
= ( HFSPlusCatalogKey
*)( buffer
+ recOffset
);
338 size_t keyLength
= S16 ( keyp
-> keyLength
);
339 // Add two because the keyLength field is not included.
340 HFSPlusCatalogFile
* fp
= ( HFSPlusCatalogFile
*)((( uint8_t *) keyp
) + 2 + keyLength
+ ( keyLength
& 1 ));
342 if ( S16 ( fp
-> recordType
) != kHFSPlusFileRecord
) {
344 fprintf ( stderr
, " %s : skipping node record %z u because it is type %#x, at offset %u keyLength %z u \n " , __FUNCTION__
, counter
, S16 ( fp
-> recordType
), recOffset
, keyLength
);
349 fprintf ( stderr
, " %s : node record %z u, file id = %u \n " , __FUNCTION__
, counter
, S32 ( fp
-> fileID
));
350 if ( S32 ( fp
-> userInfo
. fdType
) == kSymLinkFileType
&&
351 S32 ( fp
-> userInfo
. fdCreator
) == kSymLinkCreator
) {
352 unsigned int fid
= S32 ( fp
-> fileID
);
353 HFSPlusExtentDescriptor
* extPtr
= fp
-> dataFork
. extents
;
356 for ( i
= 0 ; i
< 8 ; i
++) {
357 if ( extPtr
[ i
]. startBlock
&&
358 extPtr
[ i
]. blockCount
) {
359 off_t start
= blockSize
* S32 ( extPtr
[ i
]. startBlock
);
360 off_t length
= blockSize
* S32 ( extPtr
[ i
]. blockCount
);
361 retval
= handler ( fid
, start
, length
);
374 ScanAttrNode ( VolumeObjects_t
* vop
, uint8_t * buffer
, size_t nodeSize
, extent_handler_t handler
)
376 BTNodeDescriptor
* ndp
= ( BTNodeDescriptor
*) buffer
;
377 uint16_t * indices
= ( uint16_t *)( buffer
+ nodeSize
);
379 off_t blockSize
= S32 ( vop
-> vdp
-> priHeader
. blockSize
);
382 if ( ndp
-> kind
!= kBTLeafNode
)
383 return 0 ; // Skip if it's not a leaf node
386 * Look for records of type kHFSPlusForkData and kHFSPlusAttrExtents
388 for ( counter
= 1 ; counter
<= S16 ( ndp
-> numRecords
); counter
++) {
389 // Need to get past the end of the key
391 HFSPlusAttrKey
* keyp
= ( HFSPlusAttrKey
*)( buffer
+ S16 ( indices
[- counter
]));
392 size_t keyLength
= S16 ( keyp
-> keyLength
);
393 // Add two because the keyLength field is not included.
394 HFSPlusAttrRecord
* ap
= ( HFSPlusAttrRecord
*)((( uint8_t *) keyp
) + 2 + keyLength
+ ( keyLength
& 1 ));
395 HFSPlusExtentDescriptor
* theExtents
= NULL
;
396 switch ( S32 ( ap
-> recordType
)) {
397 case kHFSPlusAttrForkData
:
398 theExtents
= ap
-> forkData
. theFork
. extents
;
400 case kHFSPlusAttrExtents
:
401 theExtents
= ap
-> overflowExtents
. extents
;
406 if ( theExtents
!= NULL
) {
407 HFSPlusExtentDescriptor
* extPtr
= theExtents
;
409 fid
= S32 ( keyp
-> fileID
);
411 for ( i
= 0 ; i
< 8 ; i
++) {
412 if ( extPtr
[ i
]. startBlock
&&
413 extPtr
[ i
]. blockCount
) {
414 off_t start
= blockSize
* S32 ( extPtr
[ i
]. startBlock
);
415 off_t length
= blockSize
* S32 ( extPtr
[ i
]. blockCount
);
416 retval
= handler ( fid
, start
, length
);
430 * Given a VolumeObject_t, search for the other metadata that
431 * aren't described by the system files, but rather in the
432 * system files. This includes symbolic links, and large EA
433 * extents. We can do this at one of two times -- while copying
434 * the data, or while setting up the list of extents. The
435 * former is going to be more efficient, but the latter will
436 * mean the estimates and continuation will be less likely to
437 * be wrong as we add extents to the list.
441 FindOtherMetadata ( VolumeObjects_t
* vop
, extent_handler_t handler
)
443 size_t catNodeSize
= 0 , attrNodeSize
= 0 ;
444 off_t node0_location
= 0 ;
447 BTNodeDescriptor
* ndp
;
450 tBuffer
= calloc ( 1 , vop
-> devp
-> blockSize
);
451 if ( tBuffer
== NULL
) {
452 warn ( "Could not allocate memory to collect extra metadata" );
456 * First, do the catalog file
458 if ( vop
-> vdp
-> priHeader
. catalogFile
. logicalSize
) {
460 node0_location
= S32 ( vop
-> vdp
-> priHeader
. catalogFile
. extents
[ 0 ]. startBlock
);
461 node0_location
= node0_location
* S32 ( vop
-> vdp
-> priHeader
. blockSize
);
462 if ( GetBlock ( vop
-> devp
, node0_location
, tBuffer
) == - 1 ) {
463 warn ( "Could not read catalog header node" );
465 ndp
= ( BTNodeDescriptor
*) tBuffer
;
466 hdp
= ( BTHeaderRec
*)( tBuffer
+ sizeof ( BTNodeDescriptor
));
468 if ( ndp
-> kind
!= kBTHeaderNode
) {
469 warnx ( "Did not read header node for catalog as expected" );
471 catNodeSize
= S16 ( hdp
-> nodeSize
);
476 * Now, the attributes file.
478 if ( vop
-> vdp
-> priHeader
. attributesFile
. logicalSize
) {
480 node0_location
= S32 ( vop
-> vdp
-> priHeader
. attributesFile
. extents
[ 0 ]. startBlock
);
481 node0_location
= node0_location
* S32 ( vop
-> vdp
-> priHeader
. blockSize
);
482 if ( GetBlock ( vop
-> devp
, node0_location
, tBuffer
) == - 1 ) {
483 warn ( "Could not read attributes file header node" );
485 ndp
= ( BTNodeDescriptor
*) tBuffer
;
486 hdp
= ( BTHeaderRec
*)( tBuffer
+ sizeof ( BTNodeDescriptor
));
488 if ( ndp
-> kind
!= kBTHeaderNode
) {
489 warnx ( "Did not read header node for attributes file as expected" );
491 attrNodeSize
= S16 ( hdp
-> nodeSize
);
496 fprintf ( stderr
, "Catalog node size = %z u, attributes node size = %z u \n " , catNodeSize
, attrNodeSize
);
499 * We start reading the extents now.
501 * This is a lot of duplicated code, unfortunately.
504 for ( exts
= vop
-> list
;
509 for ( indx
= 0 ; indx
< exts
-> count
; indx
++) {
510 off_t start
= exts
-> extents
[ indx
]. base
;
511 off_t len
= exts
-> extents
[ indx
]. length
;
513 if ( exts
-> extents
[ indx
]. fid
== 0 ) {
514 continue ; // Unknown file, skip
516 if ( debug
) fprintf ( stderr
, " %s : fid = %u , start = %l lu, len = %l lu \n " , __FUNCTION__
, exts
-> extents
[ indx
]. fid
, start
, len
);
517 while ( nread
< len
) {
520 bufSize
= MIN ( len
- nread
, 1024 * 1024 ); // Read 1mbyte max
521 buffer
= calloc ( 1 , bufSize
);
522 if ( buffer
== NULL
) {
523 warn ( "Cannot allocate %z u bytes for buffer, skipping node scan" , bufSize
);
525 ssize_t t
= UnalignedRead ( vop
-> devp
, buffer
, bufSize
, start
+ nread
);
527 warn ( "Attempted to read %z u bytes, only read %z d, skipping node scan" , bufSize
, t
);
529 uint8_t * curPtr
= buffer
, * endPtr
= ( buffer
+ bufSize
);
531 int (* func
)( VolumeObjects_t
*, uint8_t *, size_t , extent_handler_t
) = NULL
;
532 if ( exts
-> extents
[ indx
]. fid
== kHFSCatalogFileID
) {
533 func
= ScanCatalogNode
;
534 nodeSize
= catNodeSize
;
535 } else if ( exts
-> extents
[ indx
]. fid
== kHFSAttributesFileID
) {
537 nodeSize
= attrNodeSize
;
540 while ( curPtr
< endPtr
&& retval
== 0 ) {
541 retval
= (* func
)( vop
, curPtr
, nodeSize
, handler
);
563 * Perform a (potentially) unaligned read from a given input device.
567 UnalignedRead ( DeviceInfo_t
* devp
, void * buffer
, size_t size
, off_t offset
)
570 size_t readSize
= (( size
+ devp
-> blockSize
- 1 ) / devp
-> blockSize
) * devp
-> blockSize
;
571 off_t baseOffset
= ( offset
/ devp
-> blockSize
) * devp
-> blockSize
;
572 size_t off
= offset
- baseOffset
;
575 if (( baseOffset
== offset
) && ( readSize
== size
)) {
577 * The read is already properly aligned, so call pread.
579 return pread ( devp
-> fd
, buffer
, size
, offset
);
582 tmpbuf
= malloc ( readSize
);
587 nread
= pread ( devp
-> fd
, tmpbuf
, readSize
, baseOffset
);
593 if ( nread
> ( ssize_t
) size
) {
596 memcpy ( buffer
, tmpbuf
+ off
, nread
);
605 ReleaseDeviceInfo ( DeviceInfo_t
* devp
)
608 if ( devp
-> fd
!= - 1 ) {
620 ReleaseVolumeDescriptor ( VolumeDescriptor_t
* vdp
)
623 free ( vdp
); // No contained pointers!
629 ReleaseVolumeObjects ( VolumeObjects_t
* vop
)
633 ReleaseDeviceInfo ( vop
-> devp
);
636 ReleaseVolumeDescriptor ( vop
-> vdp
);
638 ExtentList_t
* extList
;
639 for ( extList
= vop
-> list
;
642 ExtentList_t
* next
= extList
-> next
;