diff --git a/ChangeLog b/ChangeLog index 601e138cb..3d61fa898 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2012-02-28 Richard Frith-Macdonald + + * Source/NSArchiver.m: + * Source/NSUnarchiver.m: + * Source/NSPortCoder.m: + * Source/Coder.m: + Updates to cope with the fact that some BSD ports of gnustep-base + used incorrect versioning, and that people have released Gorm + files with versioning incompatible with current releases. + 1. dissociate coder -systemVersion from the actual system version, + so that if people mess with the gnustep-base version, their + non-standard releases will still interoperate with normal code. + 2. Implement new array encoding/decoding compatible with earlier + versions (except if the array contains exactly 0xffffffff items). + 3. Raise exception if decoding archives with an unsupported version. + 4. Add GSCoderSystemVersion user default to override the system + version used for encoding ... for testing and tweaking to generate + archives in, or communicate with DO servers using an earlier version. + 2012-04-23 Fred Kiefer * Tests/base/NSAutoreleasePool/autorelease_eh.m: @@ -6,11 +25,11 @@ 2012-04-23 12:47 theraven * libs/base/trunk/Source/NSAutoreleasePool.m, - libs/base/trunk/Tests/base/NSAutoreleasePool/autorelease_eh.m: - Fix a bug in the new autorelease pool implementation when pools - are destroyed in the wrong order. + libs/base/trunk/Tests/base/NSAutoreleasePool/autorelease_eh.m: + Fix a bug in the new autorelease pool implementation when pools + are destroyed in the wrong order. - Test cast by Chris Armstrong! + Test cast by Chris Armstrong! 2012-04-22 Riccardo Mottola diff --git a/Source/NSArchiver.m b/Source/NSArchiver.m index e3d4fce32..56a58205c 100644 --- a/Source/NSArchiver.m +++ b/Source/NSArchiver.m @@ -254,20 +254,17 @@ static Class NSMutableDataMallocClass; count: (NSUInteger)count at: (const void*)buf { - unsigned c = count; + uint32_t c; uint8_t bytes[20]; uint8_t *bytePtr = 0; uint8_t byteCount = 0; NSUInteger i; NSUInteger offset = 0; - unsigned size = objc_sizeof_type(type); + uint32_t size = objc_sizeof_type(type); + uint32_t version = [self systemVersion]; uchar info; - /* The array count is encoded as a sequence of bytes containing 7bits of - * data and using the eighth (top) bit to indicate that there are more - * bytes in the sequence. - */ - if ([self systemVersion] > 12401) + if (12402 == version) { NSUInteger tmp = count; @@ -280,6 +277,21 @@ static Class NSMutableDataMallocClass; } bytePtr = &bytes[sizeof(bytes) - byteCount]; } + else + { + /* We normally store the count as a 32bit integer ... but if it's + * very big, we store 0xffffffff and then an additional 64bit value + * containing the actual count. + */ + if (count >= 0xffffffff) + { + c = 0xffffffff; + } + else + { + c = count; + } + } switch (*type) { @@ -308,17 +320,21 @@ static Class NSMutableDataMallocClass; if (_initialPass == NO) { (*_tagImp)(_dst, tagSel, _GSC_ARY_B); - if (0 == byteCount) - { - (*_serImp)(_dst, serSel, &c, @encode(unsigned), nil); - } - else + if (12402 == version) { for (i = 0; i < byteCount; i++) { (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil); } } + else + { + (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil); + if (0xffffffff == c) + { + (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil); + } + } } for (i = 0; i < c; i++) @@ -330,17 +346,21 @@ static Class NSMutableDataMallocClass; else if (_initialPass == NO) { (*_tagImp)(_dst, tagSel, _GSC_ARY_B); - if (0 == byteCount) - { - (*_serImp)(_dst, serSel, &c, @encode(unsigned), nil); - } - else + if (12402 == version) { for (i = 0; i < byteCount; i++) { (*_serImp)(_dst, serSel, bytePtr + i, @encode(uint8_t), nil); } } + else + { + (*_serImp)(_dst, serSel, &c, @encode(uint32_t), nil); + if (0xffffffff == c) + { + (*_serImp)(_dst, serSel, &count, @encode(NSUInteger), nil); + } + } (*_tagImp)(_dst, tagSel, info); for (i = 0; i < count; i++) diff --git a/Source/NSCoder.m b/Source/NSCoder.m index ea51424f7..f0763f6b3 100644 --- a/Source/NSCoder.m +++ b/Source/NSCoder.m @@ -36,14 +36,42 @@ #import "Foundation/NSData.h" #import "Foundation/NSCoder.h" #import "Foundation/NSSerialization.h" +#import "Foundation/NSUserDefaults.h" #import "GNUstepBase/NSObject+GNUstepBase.h" @implementation NSCoder +/* We used to use a system version which actually reflected the version + * of GNUstep-base ... but people screwed that up by releasing versions + * of base with unofficial version numbers conflicting with the scheme. + * So ... we are now starting from a basepoint of 1 million ... on the + * basis that the old numbering scheme derived from the gnustep-base + * major.minor.subminor versioning (in which each can range from 0 to 99) + * should not have allowed anyone to create an archive with a version + * greater than 999999. + * In future, the system version will change if (and only if) the format + * of the encoded data changes. + */ +#define MAX_SUPPORTED_SYSTEM_VERSION 1000000 + +static unsigned systemVersion = MAX_SUPPORTED_SYSTEM_VERSION; + + (void) initialize { if (self == [NSCoder class]) { + unsigned sv; + + /* The GSCoderSystemVersion user default is provided for testing + * and to allow new code to communicate (via Distributed Objects) + * with systems running older versions. + */ + sv = [[NSUserDefaults standardUserDefaults] + integerForKey: @"GSCoderSystemVersion"]; + if (sv > 0 && sv <= MAX_SUPPORTED_SYSTEM_VERSION) + { + systemVersion = sv; + } } } @@ -298,8 +326,7 @@ - (unsigned) systemVersion { - return (((GNUSTEP_BASE_MAJOR_VERSION * 100) - + GNUSTEP_BASE_MINOR_VERSION) * 100) + GNUSTEP_BASE_SUBMINOR_VERSION; + return systemVersion; } diff --git a/Source/NSPortCoder.m b/Source/NSPortCoder.m index 0e02ccdaa..5c36b597b 100644 --- a/Source/NSPortCoder.m +++ b/Source/NSPortCoder.m @@ -351,10 +351,16 @@ static IMP _eSerImp; /* Method to serialize with. */ static IMP _eTagImp; /* Serialize a type tag. */ static IMP _xRefImp; /* Serialize a crossref. */ +static unsigned encodingVersion; + + (void) initialize { if (self == [NSPortCoder class]) { + NSCoder *coder = [NSCoder new]; + + encodingVersion = [coder systemVersion]; + [coder release]; #if GS_WITH_GC /* We create a typed memory descriptor for map nodes. */ @@ -435,12 +441,12 @@ static IMP _xRefImp; /* Serialize a crossref. */ { NSUInteger i; NSUInteger offset = 0; - unsigned size = objc_sizeof_type(type); + uint32_t size = objc_sizeof_type(type); unsigned char info; NSUInteger count; (*_dTagImp)(_src, dTagSel, &info, 0, &_cursor); - if (_version > 12401) + if (12402 == _version) { uint8_t c; @@ -469,10 +475,15 @@ static IMP _xRefImp; /* Serialize a crossref. */ } else { - unsigned c; + uint32_t c; - (*_dDesImp)(_src, dDesSel, &c, @encode(unsigned), &_cursor, nil); + (*_dDesImp)(_src, dDesSel, &c, @encode(uint32_t), &_cursor, nil); count = c; + if (0xffffffff == c) + { + (*_dDesImp)(_src, dDesSel, + &count, @encode(NSUInteger), &_cursor, nil); + } } if (info != _GSC_ARY_B) { @@ -1135,19 +1146,16 @@ static IMP _xRefImp; /* Serialize a crossref. */ at: (const void*)buf { NSUInteger i; - unsigned c = count; + uint32_t c = count; uint8_t bytes[20]; uint8_t *bytePtr = 0; uint8_t byteCount = 0; NSUInteger offset = 0; - unsigned size = objc_sizeof_type(type); + uint32_t size = objc_sizeof_type(type); + uint32_t version = [self systemVersion]; uchar info; - /* The array count is encoded as a sequence of bytes containing 7bits of - * data and using the eighth (top) bit to indicate that there are more - * bytes in the sequence. - */ - if ([self systemVersion] > 12401) + if (12402 == version) { NSUInteger tmp = count; @@ -1160,6 +1168,21 @@ static IMP _xRefImp; /* Serialize a crossref. */ } bytePtr = &bytes[sizeof(bytes) - byteCount]; } + else + { + /* We normally store the count as a 32bit integer ... but if it's + * very big, we store 0xffffffff and then an additional 64bit value + * containing the actual count. + */ + if (count >= 0xffffffff) + { + c = 0xffffffff; + } + else + { + c = count; + } + } switch (*type) { @@ -1188,11 +1211,7 @@ static IMP _xRefImp; /* Serialize a crossref. */ if (_initialPass == NO) { (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); - if (0 == byteCount) - { - (*_eSerImp)(_dst, eSerSel, &c, @encode(unsigned), nil); - } - else + if (12402 == version) { for (i = 0; i < byteCount; i++) { @@ -1200,6 +1219,14 @@ static IMP _xRefImp; /* Serialize a crossref. */ (_dst, eSerSel, bytePtr + i, @encode(uint8_t), nil); } } + else + { + (*_eSerImp)(_dst, eSerSel, &c, @encode(uint32_t), nil); + if (0xffffffff == c) + { + (*_eSerImp)(_dst, eSerSel, &count, @encode(NSUInteger), nil); + } + } } for (i = 0; i < count; i++) { @@ -1210,17 +1237,21 @@ static IMP _xRefImp; /* Serialize a crossref. */ else if (_initialPass == NO) { (*_eTagImp)(_dst, eTagSel, _GSC_ARY_B); - if (0 == byteCount) - { - (*_eSerImp)(_dst, eSerSel, &c, @encode(unsigned), nil); - } - else + if (12402 == version) { for (i = 0; i < byteCount; i++) { (*_eSerImp)(_dst, eSerSel, bytePtr + i, @encode(uint8_t), nil); } } + else + { + (*_eSerImp)(_dst, eSerSel, &c, @encode(unsigned), nil); + if (0xffffffff == c) + { + (*_eSerImp)(_dst, eSerSel, &count, @encode(NSUInteger), nil); + } + } (*_eTagImp)(_dst, eTagSel, info); for (i = 0; i < count; i++) @@ -1920,6 +1951,12 @@ static IMP _xRefImp; /* Serialize a crossref. */ objects: &sizeO pointers: &sizeP]; + if (_version > encodingVersion) + { + [NSException raise: NSInvalidArgumentException + format: @"Message systemVersion (%u) not recognised", _version]; + } + /* * Allocate and initialise arrays to build crossref maps in. */ diff --git a/Source/NSUnarchiver.m b/Source/NSUnarchiver.m index 6686fdd7b..852f5597a 100644 --- a/Source/NSUnarchiver.m +++ b/Source/NSUnarchiver.m @@ -398,16 +398,22 @@ mapClassName(NSUnarchiverObjectInfo *info) @implementation NSUnarchiver static Class NSDataMallocClass; +static unsigned encodingVersion; + (void) initialize { if ([self class] == [NSUnarchiver class]) { + NSArchiver *archiver = [NSArchiver new]; + + encodingVersion = [archiver systemVersion]; + [archiver release]; desSel = @selector(deserializeDataAt:ofObjCType:atCursor:context:); tagSel = @selector(deserializeTypeTag:andCrossRef:atCursor:); dValSel = @selector(decodeValueOfObjCType:at:); clsDict = [[NSMutableDictionary alloc] initWithCapacity: 200]; NSDataMallocClass = [NSDataMalloc class]; + } } @@ -536,7 +542,7 @@ static Class NSDataMallocClass; NSUInteger count; (*tagImp)(src, tagSel, &info, 0, &cursor); - if ([self systemVersion] > 12401) + if ([self systemVersion] == 12402) { uint8_t c; @@ -565,10 +571,14 @@ static Class NSDataMallocClass; } else { - unsigned c; + uint32_t c; - (*desImp)(src, desSel, &c, @encode(unsigned), &cursor, nil); + (*desImp)(src, desSel, &c, @encode(uint32_t), &cursor, nil); count = c; + if (0xffffffff == c) + { + (*desImp)(src, desSel, &count, @encode(NSUInteger), &cursor, nil); + } } if (info != _GSC_ARY_B) { @@ -1524,6 +1534,11 @@ static Class NSDataMallocClass; classes: &sizeC objects: &sizeO pointers: &sizeP]; + if (version > encodingVersion) + { + [NSException raise: NSInvalidArgumentException + format: @"Archive systemVersion (%u) not recognised", version]; + } if (clsMap == 0) { diff --git a/Source/common.h b/Source/common.h index 2a09cad49..57abe809f 100644 --- a/Source/common.h +++ b/Source/common.h @@ -4,13 +4,36 @@ * might be from an earlier build. */ -// disable extensions ... we want to use standard code +/* disable extensions ... we want to use standard code + */ #ifdef _GNU_SOURCE #undef _GNU_SOURCE #endif +/* Ensure we have _XOPEN_SOURCE turned on at the appropriate + * level for the facilities we need. + * + * Minimum of 600 for string.h so we get the POSIX strerror_r() behavior + */ +#if defined(_XOPEN_SOURCE) +#if _XOPEN_SOURCE < 600 +#undef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 +#endif +#else +#define _XOPEN_SOURCE 600 +#endif + #import "config.h" +#if defined(HAVE_STRING_H) +/* For POSIX strerror_r() + */ +#include +#endif + +#include + /* If this is included in a file in the Additions subdirectory, and we are * building for use with the NeXT/Apple Foundation, then we need to import * the native headers in preference to any of our own.