mirror of
https://github.com/MacPaw/XADMaster.git
synced 2025-08-29 03:23:48 +02:00
649 lines
22 KiB
Objective-C
649 lines
22 KiB
Objective-C
/*
|
|
* XADTarParser.m
|
|
*
|
|
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
// Parses tar files, except: Star, sparse tar files.
|
|
|
|
#import "XADTarParser.h"
|
|
|
|
#define TAR_FORMAT_V7 0
|
|
#define TAR_FORMAT_GNU 1 // GNU and OLDGNU are basically identical
|
|
#define TAR_FORMAT_USTAR 2 // POSIX-ish tar formats
|
|
#define TAR_FORMAT_STAR 3 // STAR is POSIX-ish, but not similiar enough to ustar and posix.2001 tar.
|
|
#define TAR_FORMAT_V7_RECOGNIZED 4
|
|
|
|
// TODO: star.
|
|
|
|
@implementation XADTarParser
|
|
|
|
+(int)requiredHeaderSize { return 512; }
|
|
|
|
+(int)getTarType:(NSData *)header
|
|
{
|
|
unsigned char head[512];
|
|
[header getBytes:head length:512];
|
|
|
|
int tarFormat = TAR_FORMAT_V7;
|
|
unsigned char magic[8];
|
|
[header getBytes:magic range:NSMakeRange(257,8)]; // "ustar\000" (ustar) / "ustar \0" (gnu)
|
|
|
|
unsigned char starExtendedMagic[4];
|
|
[header getBytes:starExtendedMagic range:NSMakeRange(508,4)]; // "tar\0"
|
|
|
|
unsigned int checksum = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(148,8) buffer:header];
|
|
if( [XADTarParser isTarChecksumCorrect:header checksum:checksum] == YES )
|
|
{
|
|
tarFormat = TAR_FORMAT_V7_RECOGNIZED;
|
|
}
|
|
|
|
if( memcmp( magic, (unsigned char[]){ 117, 115, 116, 97, 114, 0, 48, 48 }, 8 ) == 0 )
|
|
{
|
|
if( memcmp( starExtendedMagic, (unsigned char[]){ 116, 97, 114, 0 }, 4 ) == 0 )
|
|
{
|
|
tarFormat = TAR_FORMAT_STAR;
|
|
}
|
|
else
|
|
{
|
|
// printf("TAR: Ustar format.\n" );
|
|
tarFormat = TAR_FORMAT_USTAR;
|
|
}
|
|
}
|
|
else if( memcmp( magic, (unsigned char[]){ 117, 115, 116, 97, 114, 32, 32, 0 }, 8 ) == 0 )
|
|
{
|
|
tarFormat = TAR_FORMAT_GNU;
|
|
}
|
|
|
|
return( tarFormat );
|
|
}
|
|
|
|
// Recognize files by name or magic. (tar v7 files have no magic, are recognized as
|
|
// TAR_FORMAT_V7_RECOGNIZED via checksums)
|
|
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
|
|
{
|
|
if([data length]<512) return NO;
|
|
return(
|
|
[name matchedByPattern:@"^(.*)\\.tar$" options:REG_ICASE] ||
|
|
([XADTarParser getTarType:data] != TAR_FORMAT_V7)
|
|
);
|
|
}
|
|
|
|
+(double)doubleFromCString:(char *)buffer
|
|
{
|
|
NSString *readString = [[NSString alloc] initWithUTF8String:buffer];
|
|
NSScanner* scanner = [NSScanner scannerWithString:readString];
|
|
[readString release];
|
|
double returnValue;
|
|
if([scanner scanDouble:&returnValue] == YES) {
|
|
return( returnValue );
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
+(int64_t)longFromCString:(char *)buffer
|
|
{
|
|
NSString *readString = [[NSString alloc] initWithUTF8String:buffer];
|
|
NSScanner* scanner = [NSScanner scannerWithString:readString];
|
|
[readString release];
|
|
long long returnValue;
|
|
if([scanner scanLongLong:&returnValue] == YES) {
|
|
return( returnValue );
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
+(int64_t)readNumberInRangeFromBuffer:(NSRange)range buffer:(NSData *)buffer
|
|
{
|
|
NSString *readString = [[NSString alloc] initWithData:[buffer subdataWithRange:range] encoding:NSASCIIStringEncoding];
|
|
if(!readString) return 0;
|
|
NSScanner* scanner = [NSScanner scannerWithString:readString];
|
|
[readString release];
|
|
long long returnValue;
|
|
if([scanner scanLongLong:&returnValue] == YES) {
|
|
return( returnValue );
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
+(int64_t)octalToDecimal:(int64_t)octal
|
|
{
|
|
int64_t decimal = 0;
|
|
int64_t temp = 0;
|
|
int64_t power_of_ten = 10000000000000;
|
|
int64_t power_of_eight = 549755813888;
|
|
while( power_of_ten != 1 )
|
|
{
|
|
power_of_ten = power_of_ten / 10;
|
|
power_of_eight = power_of_eight / 8;
|
|
temp = octal / power_of_ten;
|
|
decimal += temp * power_of_eight;
|
|
octal -= temp * power_of_ten;
|
|
}
|
|
return( decimal );
|
|
}
|
|
|
|
+(uint64_t)readOctalNumberInRangeFromBuffer:(NSRange)range buffer:(NSData *)buffer
|
|
{
|
|
// This implementation only supports numbers up to 2^64. If we ever need bigger numbers,
|
|
// that needs to change.
|
|
// Also, negative values are not supported. This could be an issue if the mtime field was
|
|
// supposed to be negative, but it is probably fair to assume that we will not be
|
|
// extracting star archives with files last modified before 1970.
|
|
uint8_t num_string[8];
|
|
uint8_t mask = 0x80;
|
|
[buffer getBytes:num_string range:NSMakeRange(range.location,8)];
|
|
if( (num_string[0]&mask) == 0x80 ) {
|
|
[buffer getBytes:num_string range:NSMakeRange(range.location + range.length - 8,8)];
|
|
return CSUInt64BE(num_string);
|
|
}
|
|
return( [XADTarParser octalToDecimal:[XADTarParser readNumberInRangeFromBuffer:range buffer:buffer]] );
|
|
}
|
|
|
|
// "Sum of all header bytes if the checksum field was 8 * ' '".
|
|
+(BOOL)isTarChecksumCorrect:(NSData *)header checksum:(int)checksum
|
|
{
|
|
unsigned char head[512];
|
|
[header getBytes:head length:512];
|
|
|
|
int signedChecksum = 0;
|
|
unsigned int unsignedChecksum = 0;
|
|
for( int i = 0; i < 148; i++ )
|
|
{
|
|
signedChecksum += (signed char)head[i];
|
|
unsignedChecksum += (unsigned char)head[i];
|
|
}
|
|
|
|
for( int i = 156; i < 512; i++ )
|
|
{
|
|
signedChecksum += (signed char)head[i];
|
|
unsignedChecksum += (unsigned char)head[i];
|
|
}
|
|
|
|
signedChecksum += 8 * ' ';
|
|
unsignedChecksum += 8 * ' ';
|
|
|
|
return( checksum == signedChecksum || checksum == unsignedChecksum );
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[currentGlobalHeader release];
|
|
[super dealloc];
|
|
}
|
|
|
|
-(void)parseSparseHeadersFromData:(NSData*)header numHeaders:(int)num toDict:(NSMutableDictionary *)dict
|
|
{
|
|
for( int i = 0; i < num; i++ )
|
|
{
|
|
|
|
off_t regionOffset = [XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(24*i,12) buffer:header];
|
|
off_t regionLength = [XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(24*i+12,12) buffer:header];
|
|
|
|
// printf( "Parse sparse header: %d -> %d\n", regionOffset, regionLength );
|
|
|
|
// Are we done yet?
|
|
if( regionLength == 0 && regionOffset == 0 ) {
|
|
break;
|
|
}
|
|
|
|
[[dict objectForKey:@"TARSparseRegionOffsets"] addObject:[NSNumber numberWithLongLong:regionOffset]];
|
|
[[dict objectForKey:@"TARSparseRegionLengths"] addObject:[NSNumber numberWithLongLong:regionLength]];
|
|
}
|
|
}
|
|
|
|
-(int)parseGenericTarHeader:(NSData *)header toDict:(NSMutableDictionary *)dict
|
|
{
|
|
char name[101];
|
|
[header getBytes:name range:NSMakeRange(0,100)];
|
|
name[100] = '\000';
|
|
[dict setObject:[self XADPathWithCString:name separators:XADUnixPathSeparator] forKey:XADFileNameKey];
|
|
|
|
unsigned int mode = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(100,8) buffer:header];
|
|
[dict setObject:[NSNumber numberWithInt:mode] forKey:XADPosixPermissionsKey];
|
|
|
|
unsigned int uid = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(108,8) buffer:header];
|
|
[dict setObject:[NSNumber numberWithInt:uid] forKey:XADPosixUserKey];
|
|
|
|
unsigned int gid = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(116,8) buffer:header];
|
|
[dict setObject:[NSNumber numberWithInt:gid] forKey:XADPosixGroupKey];
|
|
|
|
uint64_t size = [XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(124,12) buffer:header];
|
|
|
|
[dict setObject:[NSNumber numberWithLongLong:size] forKey:XADFileSizeKey];
|
|
[dict setObject:[NSNumber numberWithLongLong:(size+(512-size%512))] forKey:XADCompressedSizeKey];
|
|
[dict setObject:[NSNumber numberWithLongLong:size] forKey:XADDataLengthKey];
|
|
|
|
unsigned long mtime = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(136,12) buffer:header];
|
|
[dict setObject:[NSDate dateWithTimeIntervalSince1970:mtime] forKey:XADLastModificationDateKey];
|
|
|
|
unsigned int checksum = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(148,8) buffer:header];
|
|
if( [XADTarParser isTarChecksumCorrect:header checksum:checksum] == NO )
|
|
{
|
|
[XADException raiseIllegalDataException];
|
|
}
|
|
|
|
char typeFlag;
|
|
[header getBytes:&typeFlag range:NSMakeRange(156,1)];
|
|
|
|
// There are only two type flags that tar, in general, supports.
|
|
// "Directory"
|
|
if( typeFlag == '5' )
|
|
{
|
|
[dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsDirectoryKey];
|
|
}
|
|
|
|
// "Hard link / soft link"
|
|
if( typeFlag == '1' || typeFlag == '2' )
|
|
{
|
|
char linkName[101];
|
|
[header getBytes:linkName range:NSMakeRange(157,100)];
|
|
linkName[100] = '\000';
|
|
[dict setObject:[self XADStringWithCString:linkName] forKey:XADLinkDestinationKey];
|
|
[dict setObject:[NSNumber numberWithInt:(typeFlag%2)] forKey:XADIsHardLinkKey];
|
|
}
|
|
|
|
// KLUDGE: Fix broken directory typeflags.
|
|
if( typeFlag == 0 && name[strlen(name)-1] == '/' ) {
|
|
[dict setObject:[NSNumber numberWithBool:YES] forKey:XADIsDirectoryKey];
|
|
}
|
|
|
|
// KLUDGE: Fix broken ant tar wrong magic files.
|
|
if( strncmp( name, "././@LongLink", 13 ) == 0 ) {
|
|
return( 1 );
|
|
}
|
|
|
|
// printf( "Generictar: Name %s / %u\n", name, size );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
-(void)parsePaxTarHeader:(NSData *)header toDict:(NSMutableDictionary *)dict {
|
|
int position = 0;
|
|
while( position < [header length] ) {
|
|
// Get next pair length.
|
|
int start_pos = position;
|
|
int read_length = 0;
|
|
char current_char = '\0';
|
|
while( current_char != ' ' && current_char != 0 && read_length < 16 && position < [header length] ) {
|
|
[header getBytes:¤t_char range:NSMakeRange(position,1)];
|
|
position++;
|
|
read_length++;
|
|
}
|
|
if( read_length == 0 ){
|
|
break;
|
|
}
|
|
|
|
// Grab the pair from the header.
|
|
int next_pair_size = (int)[XADTarParser readNumberInRangeFromBuffer:NSMakeRange(start_pos,read_length) buffer:header] - read_length;
|
|
int next_pair_offset = position + next_pair_size;
|
|
char key_val_pair[next_pair_size];
|
|
memset( key_val_pair, '\0', next_pair_size );
|
|
[header getBytes:key_val_pair range:NSMakeRange(position,next_pair_size - 1)];
|
|
|
|
// Parse the pair into key/value.
|
|
read_length = 0;
|
|
//start_pos = position;
|
|
int max_len = strlen( key_val_pair );
|
|
while( key_val_pair[read_length] != '=' && read_length < max_len ) {
|
|
position++;
|
|
read_length++;
|
|
}
|
|
char key[read_length + 1];
|
|
memcpy( key, key_val_pair, read_length );
|
|
key[read_length] = '\0';
|
|
char value[max_len - read_length + 1];
|
|
memcpy( value, key_val_pair + read_length + 1, (max_len - read_length) );
|
|
value[(max_len - read_length)] = '\0';
|
|
|
|
// Check keys and add proper value to dict.
|
|
// Accessed/Created/Last Modified
|
|
if( strcmp( key, "atime" ) == 0 ) {
|
|
[dict setObject:[NSDate dateWithTimeIntervalSince1970:[XADTarParser doubleFromCString:value]] forKey:XADLastAccessDateKey];
|
|
}
|
|
else if( strcmp( key, "ctime" ) == 0 ) {
|
|
[dict setObject:[NSDate dateWithTimeIntervalSince1970:[XADTarParser doubleFromCString:value]] forKey:XADCreationDateKey];
|
|
}
|
|
else if( strcmp( key, "mtime" ) == 0 ) {
|
|
[dict setObject:[NSDate dateWithTimeIntervalSince1970:[XADTarParser doubleFromCString:value]] forKey:XADLastModificationDateKey];
|
|
}
|
|
|
|
// User/Group ids/names.
|
|
else if( strcmp( key, "uname" ) == 0 ) {
|
|
[dict setObject:[self XADStringWithCString:value encodingName:XADUTF8StringEncodingName] forKey:XADPosixUserNameKey];
|
|
}
|
|
else if( strcmp( key, "gname" ) == 0 ) {
|
|
[dict setObject:[self XADStringWithCString:value encodingName:XADUTF8StringEncodingName] forKey:XADPosixGroupNameKey];
|
|
}
|
|
else if( strcmp( key, "uid" ) == 0 ) {
|
|
[dict setObject:[NSNumber numberWithInt:(int)[XADTarParser longFromCString:value]] forKey:XADPosixUserKey];
|
|
}
|
|
else if( strcmp( key, "gid" ) == 0 ) {
|
|
[dict setObject:[NSNumber numberWithInt:(int)[XADTarParser longFromCString:value]] forKey:XADPosixGroupKey];
|
|
}
|
|
|
|
// File path and link path.
|
|
else if( strcmp( key, "path" ) == 0 ) {
|
|
[dict setObject:[self XADPathWithCString:value encodingName:XADUTF8StringEncodingName separators:XADUnixPathSeparator] forKey:XADFileNameKey];
|
|
}
|
|
else if( strcmp( key, "linkpath" ) == 0 ) {
|
|
[dict setObject:[self XADStringWithCString:value encodingName:XADUTF8StringEncodingName] forKey:XADLinkDestinationKey];
|
|
}
|
|
|
|
// File size.
|
|
else if( strcmp( key, "size" ) == 0 ) {
|
|
[dict setObject:[NSNumber numberWithInt:(int)[XADTarParser longFromCString:value]] forKey:XADFileSizeKey];
|
|
}
|
|
|
|
// Comment.
|
|
else if( strcmp( key, "comment" ) == 0 ) {
|
|
[dict setObject:[self XADStringWithCString:value encodingName:XADUTF8StringEncodingName] forKey:XADCommentKey];
|
|
}
|
|
|
|
// Continue after the pair.
|
|
|
|
position = next_pair_offset;
|
|
}
|
|
}
|
|
|
|
-(void)parseUstarTarHeader:(NSData *)header toDict:(NSMutableDictionary *)dict
|
|
{
|
|
char userName[33];
|
|
[header getBytes:userName range:NSMakeRange(265,32)];
|
|
userName[32] = '\000';
|
|
[dict setObject:[self XADStringWithCString:userName] forKey:XADPosixUserNameKey];
|
|
|
|
char groupName[33];
|
|
[header getBytes:groupName range:NSMakeRange(297,32)];
|
|
groupName[32] = '\000';
|
|
[dict setObject:[self XADStringWithCString:groupName] forKey:XADPosixGroupNameKey];
|
|
|
|
unsigned int devMajor = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(329,8) buffer:header];
|
|
|
|
unsigned int devMinor = (unsigned int)[XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(337,8) buffer:header];
|
|
|
|
char prefix[156];
|
|
[header getBytes:prefix range:NSMakeRange(345,155)];
|
|
prefix[155] = '\000';
|
|
|
|
// Prefix is not null => name is prefix . name
|
|
char fullName[257];
|
|
if( !(prefix[0] == '\000') ) {
|
|
char name[101];
|
|
[header getBytes:name range:NSMakeRange(0,100)];
|
|
name[100] = '\000';
|
|
fullName[0] = '\000';
|
|
strcat( fullName, prefix );
|
|
strcat( fullName, "/" );
|
|
strcat( fullName, name );
|
|
fullName[256] = '\000';
|
|
[dict setObject:[self XADPathWithCString:fullName separators:XADUnixPathSeparator] forKey:XADFileNameKey];
|
|
}
|
|
|
|
char typeFlag;
|
|
[header getBytes:&typeFlag range:NSMakeRange(156,1)];
|
|
|
|
// Global header parse.
|
|
[self parsePaxTarHeader:currentGlobalHeader toDict:dict];
|
|
|
|
// printf( "Ustar header parse after global\n" );
|
|
|
|
// Needed later for extended headers, possibly.
|
|
CSHandle *handle = [self handle];
|
|
long size = [[dict objectForKey:XADDataLengthKey] longValue];
|
|
off_t offset = [handle offsetInFile];;
|
|
offset += size;
|
|
offset += (offset % 512 == 0 ? 0 : 512 - (offset % 512) );
|
|
|
|
switch( typeFlag ) {
|
|
// Device files
|
|
case '3':
|
|
[dict setObject:[NSNumber numberWithInt:1] forKey:XADIsCharacterDeviceKey];
|
|
[dict setObject:[NSNumber numberWithInt:devMajor] forKey:XADDeviceMajorKey];
|
|
[dict setObject:[NSNumber numberWithInt:devMinor] forKey:XADDeviceMinorKey];
|
|
break;
|
|
case '4':
|
|
[dict setObject:[NSNumber numberWithInt:1] forKey:XADIsBlockDeviceKey];
|
|
[dict setObject:[NSNumber numberWithInt:devMajor] forKey:XADDeviceMajorKey];
|
|
[dict setObject:[NSNumber numberWithInt:devMinor] forKey:XADDeviceMinorKey];
|
|
break;
|
|
|
|
// FIFOs
|
|
case '6':
|
|
[dict setObject:[NSNumber numberWithInt:1] forKey:XADIsFIFOKey];
|
|
break;
|
|
|
|
// POSIX.2001 global header.
|
|
case 'g': {
|
|
// Read in the header and store for parsing
|
|
[currentGlobalHeader release];
|
|
currentGlobalHeader = [[handle readDataOfLength:size] retain];
|
|
[handle seekToFileOffset:offset];
|
|
|
|
// Parse next header.
|
|
NSData *header = [handle readDataOfLength:512];
|
|
[dict removeAllObjects];
|
|
[self parseGenericTarHeader:header toDict:dict];
|
|
[self parseUstarTarHeader:header toDict:dict];
|
|
} break;
|
|
|
|
// POSIX.2001 extended header.
|
|
case 'x': {
|
|
// Read in the header.
|
|
NSData *extendedHeader = [handle readDataOfLength:size];
|
|
[handle seekToFileOffset:offset];
|
|
|
|
// Prepare a new dictionary with the next header.
|
|
NSData *header = [handle readDataOfLength:512];
|
|
[dict removeAllObjects];
|
|
[self parseGenericTarHeader:header toDict:dict];
|
|
[self parseUstarTarHeader:header toDict:dict];
|
|
|
|
// Parse extended header.
|
|
[self parsePaxTarHeader:extendedHeader toDict:dict];
|
|
} break;
|
|
}
|
|
}
|
|
|
|
-(void)parseGnuTarHeader:(NSData *)header toDict:(NSMutableDictionary *)dict
|
|
{
|
|
char typeFlag;
|
|
[header getBytes:&typeFlag range:NSMakeRange(156,1)];
|
|
|
|
// In case of LongName / LongLink, we need the data.
|
|
CSHandle *handle = [self handle];
|
|
long size = [[dict objectForKey:XADDataLengthKey] longValue];
|
|
off_t offset = [handle offsetInFile];
|
|
offset += size;
|
|
offset += (offset % 512 == 0 ? 0 : 512 - (offset % 512) );
|
|
|
|
// LongName or LongLink?
|
|
if( typeFlag == 'L' || typeFlag == 'K' ) {
|
|
// Read in the header
|
|
NSData *longHeader = [handle readDataOfLength:size];
|
|
[handle seekToFileOffset:offset];
|
|
|
|
// Check if there is a terminating null byte, and eliminate it.
|
|
int length=[longHeader length];
|
|
const uint8_t *bytes=[longHeader bytes];
|
|
if(length>0 && bytes[length-1]==0) longHeader=[longHeader subdataWithRange:NSMakeRange(0,length-1)];
|
|
|
|
// Prepare a new dictionary with the next header.
|
|
NSData *header = [handle readDataOfLength:512];
|
|
[dict removeAllObjects];
|
|
[self parseGenericTarHeader:header toDict:dict];
|
|
[self parseGnuTarHeader:header toDict:dict];
|
|
|
|
// Set the proper key.
|
|
if( typeFlag == 'L' ) {
|
|
[dict setObject:[self XADPathWithData:longHeader separators:XADUnixPathSeparator] forKey:XADFileNameKey];
|
|
}
|
|
else {
|
|
[dict setObject:[self XADStringWithData:longHeader] forKey:XADLinkDestinationKey];
|
|
}
|
|
}
|
|
|
|
// Sparse header?
|
|
if( typeFlag == 'S' ) {
|
|
// For now, pass out a "Not supported".
|
|
|
|
// // Get and store real size.
|
|
// off_t size = [XADTarParser readOctalNumberInRangeFromBuffer:NSMakeRange(483,12) buffer:header];
|
|
// [dict setObject:[NSNumber numberWithLongLong:size] forKey:XADFileSizeKey];
|
|
// [dict setObject:[NSNumber numberWithLongLong:size] forKey:XADDataLengthKey];
|
|
//
|
|
// // Set up sparse map storage.
|
|
[dict setObject:[NSNumber numberWithBool:YES] forKey:@"TARIsSparseFile"];
|
|
// [dict setObject:[[NSMutableArray alloc] init] forKey:@"TARSparseRegionOffsets"];
|
|
// [dict setObject:[[NSMutableArray alloc] init] forKey:@"TARSparseRegionLengths"];
|
|
//
|
|
// NSData* sparseMap = [header subdataWithRange:NSMakeRange(386,96)];
|
|
// [self parseSparseHeadersFromData:sparseMap numHeaders:4 toDict:dict];
|
|
//
|
|
// // Handle extended sparse headers.
|
|
// char hasExtended;
|
|
// [header getBytes:&hasExtended range:NSMakeRange(482,1)];
|
|
// printf( "Hasx: %d\n", hasExtended );
|
|
// while( hasExtended == 1 ) {
|
|
// NSData *sparseMap = [handle readDataOfLength:512];
|
|
// [self parseSparseHeadersFromData:sparseMap numHeaders:21 toDict:dict];
|
|
// [sparseMap getBytes:&hasExtended range:NSMakeRange(504,1)];
|
|
// }
|
|
}
|
|
}
|
|
|
|
-(void)addTarEntryWithDictionaryAndSeek:(NSMutableDictionary *)dict
|
|
{
|
|
CSHandle *handle = [self handle];
|
|
off_t size = [[dict objectForKey:XADDataLengthKey] longLongValue];
|
|
off_t offset = [handle offsetInFile];
|
|
[dict setObject:[NSNumber numberWithLongLong:offset] forKey:XADDataOffsetKey];
|
|
[self addEntryWithDictionary:dict];
|
|
offset += size;
|
|
offset += (offset % 512 == 0 ? 0 : 512 - (offset % 512) );
|
|
[handle seekToFileOffset:offset];
|
|
}
|
|
|
|
-(void)parseWithSeparateMacForks
|
|
{
|
|
// Reset global current header for posix.2001;
|
|
currentGlobalHeader = [NSData new];
|
|
|
|
CSHandle *handle = [self handle];
|
|
int tarFormat = -1;
|
|
|
|
while([self shouldKeepParsing])
|
|
{
|
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
|
|
|
// Read next header.
|
|
if([handle atEndOfFile]) break;
|
|
NSData *header = [handle readDataOfLength:512];
|
|
|
|
// Figure out format if we haven't yet done so.
|
|
if( tarFormat == -1 ) tarFormat = [XADTarParser getTarType:header];
|
|
|
|
// See if there are 512 nullbytes. That means the file is over.
|
|
const char *firstBytes = [header bytes];
|
|
BOOL isArchiverOver = YES;
|
|
for( int i = 0; i < 512; i++ ) {
|
|
if( firstBytes[i] != '\000' ) {
|
|
isArchiverOver = NO;
|
|
}
|
|
}
|
|
if( isArchiverOver )
|
|
{
|
|
[pool release];
|
|
break;
|
|
}
|
|
|
|
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
|
|
|
// Reset sparseity.
|
|
[dict setObject:[NSNumber numberWithBool:NO] forKey:@"TARIsSparseFile"];
|
|
|
|
int wrongFormat = [self parseGenericTarHeader:header toDict:dict];
|
|
if( wrongFormat == 1 ) {
|
|
tarFormat = TAR_FORMAT_GNU;
|
|
}
|
|
|
|
if( tarFormat == TAR_FORMAT_V7 || tarFormat == TAR_FORMAT_V7_RECOGNIZED ) {
|
|
[self addTarEntryWithDictionaryAndSeek:dict];
|
|
}
|
|
else if( tarFormat == TAR_FORMAT_USTAR )
|
|
{
|
|
[self parseUstarTarHeader:header toDict:dict];
|
|
[self addTarEntryWithDictionaryAndSeek:dict];
|
|
}
|
|
else if( tarFormat == TAR_FORMAT_GNU )
|
|
{
|
|
[self parseGnuTarHeader:header toDict:dict];
|
|
[self addTarEntryWithDictionaryAndSeek:dict];
|
|
}
|
|
else
|
|
{
|
|
// Handled like an ustar archive.
|
|
[self parseUstarTarHeader:header toDict:dict];
|
|
[self addTarEntryWithDictionaryAndSeek:dict];
|
|
}
|
|
|
|
[pool release];
|
|
}
|
|
}
|
|
|
|
-(CSHandle *)rawHandleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
|
|
{
|
|
CSHandle* retHandle = [self handleAtDataOffsetForDictionary:dict];
|
|
if( [[dict objectForKey:@"TARIsSparseFile"] boolValue] ) {
|
|
// Just return nil.
|
|
return( nil );
|
|
|
|
// XADTarSparseHandle* sparseHandle = [[XADTarSparseHandle alloc] initWithHandle:retHandle size:[[dict objectForKey:XADDataLengthKey] longValue]];
|
|
// NSArray* offsets = [dict objectForKey:@"TARSparseRegionOffsets"];
|
|
// NSArray* lengths = [dict objectForKey:@"TARSparseRegionLengths"];
|
|
// int sparseRegionCount = [offsets count];
|
|
// for( int i = 0; i < sparseRegionCount; i++ ) {
|
|
// // GNU tar is pretty special about some things.
|
|
// if( [[lengths objectAtIndex:i] longValue] == 0 )
|
|
// {
|
|
// // The final region is also the first - special case!
|
|
// if( i == 0 )
|
|
// {
|
|
// printf( "Final region add (not really)!\n" );
|
|
// [sparseHandle setSingleEmptySparseRegion];
|
|
// }
|
|
// else
|
|
// {
|
|
// printf( "Final region add!\n" );
|
|
// [sparseHandle addFinalSparseRegionEndingAt:[[offsets objectAtIndex:i] longValue]];
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// printf( "Normal region add!\n" );
|
|
// [sparseHandle addSparseRegionFrom:[[offsets objectAtIndex:i] longValue] length:[[lengths objectAtIndex:i] longValue]];
|
|
// }
|
|
// }
|
|
// return( sparseHandle );
|
|
}
|
|
// printf( "Handle return" );
|
|
return( retHandle );
|
|
}
|
|
// This should maybe return USTAR or POSIX Tar or whatever the proper tar type is.
|
|
-(NSString *)formatName { return @"Tar"; }
|
|
|
|
@end
|