ObjFW  Check-in [def4cbbba6]

Overview
Comment:OFZIPArchive: Support for writing ZIP64 archives
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: def4cbbba67f874ec79351d63b5e90062e5c854d19bf239882785cf21ffe9203
User & Date: js on 2017-08-14 00:31:05
Other Links: manifest | tags
Context
2017-08-14
00:45
OFZIPArchive(Entry): A logic and a typo fix check-in: de6e2319ca user: js tags: trunk
00:31
OFZIPArchive: Support for writing ZIP64 archives check-in: def4cbbba6 user: js tags: trunk
2017-08-13
22:38
Fix -[OFData description] check-in: 484c7987d2 user: js tags: trunk
Changes

Modified src/OFZIPArchive.m from [c1fa8a1f8d] to [db5f1942c0].

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
43
44
45
46
47
48
49

50

51
52
53
54
55
56
57







-

-







#import "OFOutOfRangeException.h"
#import "OFSeekFailedException.h"
#import "OFUnsupportedVersionException.h"

/*
 * FIXME: Current limitations:
 *  - Split archives are not supported.
 *  - Write support is missing.
 *  - Encrypted files cannot be read.
 *  - No support for writing ZIP64 files.
 */

@interface OFZIPArchive ()
- (void)of_readZIPInfo;
- (void)of_readEntries;
- (void)of_closeLastReturnedStream;
- (void)of_writeCentralDirectory;
471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489
490
469
470
471
472
473
474
475
476
477
478
479
480
481

482
483
484
485
486
487
488







+





-








	return [[_lastReturnedStream retain] autorelease];
}

- (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry_
{
	/* TODO: Avoid data descriptor when _stream is an OFSeekableStream */
	int64_t offsetAdd = 0;
	void *pool;
	OFMutableZIPArchiveEntry *entry;
	OFString *fileName;
	OFData *extraField;
	uint16_t fileNameLength, extraFieldLength;
	int64_t offsetAdd = 0;

	if (_mode != OF_ZIP_ARCHIVE_MODE_WRITE &&
	    _mode != OF_ZIP_ARCHIVE_MODE_APPEND)
		@throw [OFInvalidArgumentException exception];

	pool = objc_autoreleasePoolPush();
	entry = [[entry_ mutableCopy] autorelease];
498
499
500
501
502
503
504




505


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527







528
529
530


531
532
533
534
535
536







537
538
539
540
541

542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574









575
576









577

578
579
580
581
582
583
584






585
586
587
588
589
590
591
496
497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525





526
527
528
529
530
531
532
533


534
535


536



537
538
539
540
541
542
543
544

545
546

547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586


587
588
589
590
591
592
593
594
595
596
597
598






599
600
601
602
603
604
605
606
607
608
609
610
611







+
+
+
+
-
+
+

















-
-
-
-
-
+
+
+
+
+
+
+

-
-
+
+
-
-

-
-
-
+
+
+
+
+
+
+

-


-
+



















-
-
-











+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+

+

-
-
-
-
-
-
+
+
+
+
+
+







	if ([entry compressionMethod] !=
	    OF_ZIP_ARCHIVE_ENTRY_COMPRESSION_METHOD_NONE)
		@throw [OFNotImplementedException exceptionWithSelector: _cmd
								 object: self];

	[self of_closeLastReturnedStream];

	fileName = [entry fileName];
	fileNameLength = [fileName UTF8StringLength];
	extraField = [entry extraField];
	extraFieldLength = [extraField count];
	if (_offset > UINT32_MAX)

	if (UINT16_MAX - extraFieldLength < 20)
		@throw [OFOutOfRangeException exception];

	[entry setVersionMadeBy: ([entry versionMadeBy] & 0xFF00) | 45];
	[entry setMinVersionNeeded: ([entry minVersionNeeded] & 0xFF00) | 45];
	[entry setCompressedSize: 0];
	[entry setUncompressedSize: 0];
	[entry setCRC32: 0];
	[entry setGeneralPurposeBitFlag:
	    [entry generalPurposeBitFlag] | (1 << 3) | (1 << 11)];
	[entry of_setLocalFileHeaderOffset: _offset];

	[_stream writeLittleEndianInt32: 0x04034B50];
	[_stream writeLittleEndianInt16: [entry minVersionNeeded]];
	[_stream writeLittleEndianInt16: [entry generalPurposeBitFlag]];
	[_stream writeLittleEndianInt16: [entry compressionMethod]];
	[_stream writeLittleEndianInt16: [entry of_lastModifiedFileTime]];
	[_stream writeLittleEndianInt16: [entry of_lastModifiedFileDate]];
	/* We use the data descriptor */
	[_stream writeLittleEndianInt32: 0];
	[_stream writeLittleEndianInt32: 0];
	[_stream writeLittleEndianInt32: 0];
	offsetAdd += 4 + (5 * 2) + (3 * 4);
	/* We use ZIP64 */
	[_stream writeLittleEndianInt32: 0xFFFFFFFF];
	[_stream writeLittleEndianInt32: 0xFFFFFFFF];
	[_stream writeLittleEndianInt32: 0xFFFFFFFF];
	[_stream writeLittleEndianInt16: fileNameLength];
	[_stream writeLittleEndianInt16: extraFieldLength + 20];
	offsetAdd += 4 + (5 * 2) + (3 * 4) + (2 * 2);

	fileName = [entry fileName];
	fileNameLength = [fileName UTF8StringLength];
	[_stream writeString: fileName];
	offsetAdd += fileNameLength;
	extraField = [entry extraField];
	extraFieldLength = [extraField count];

	[_stream writeLittleEndianInt16: fileNameLength];
	[_stream writeLittleEndianInt16: extraFieldLength];
	offsetAdd += 2 * 2;
	[_stream writeLittleEndianInt16:
	    OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64];
	[_stream writeLittleEndianInt16: 16];
	/* We use the data descriptor */
	[_stream writeLittleEndianInt64: 0];
	[_stream writeLittleEndianInt64: 0];
	offsetAdd += (2 * 2) + (2 * 8);

	[_stream writeString: fileName];
	if (extraField != nil)
		[_stream writeData: extraField];
	offsetAdd += fileNameLength + extraFieldLength;
	offsetAdd += extraFieldLength;

	if (INT64_MAX - _offset < offsetAdd)
		@throw [OFOutOfRangeException exception];

	_offset += offsetAdd;

	_lastReturnedStream = [[OFZIPArchive_FileWriteStream alloc]
	     initWithStream: _stream
		      entry: entry];

	objc_autoreleasePoolPop(pool);

	return [[_lastReturnedStream retain] autorelease];
}

- (void)of_writeCentralDirectory
{
	void *pool = objc_autoreleasePoolPush();

	if (_offset > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	_centralDirectoryEntries = 0;
	_centralDirectoryEntriesInDisk = 0;
	_centralDirectorySize = 0;
	_centralDirectoryOffset = _offset;

	for (OFZIPArchiveEntry *entry in _entries) {
		_centralDirectorySize += [entry of_writeToStream: _stream];
		_centralDirectoryEntries++;
		_centralDirectoryEntriesInDisk++;
	}

	/* ZIP64 end of central directory */
	[_stream writeLittleEndianInt32: 0x06064B50];
	[_stream writeLittleEndianInt64: 44];	/* Remaining size */
	[_stream writeLittleEndianInt16: 45];	/* Version made by */
	[_stream writeLittleEndianInt16: 45];	/* Version required */
	[_stream writeLittleEndianInt32: _diskNumber];
	[_stream writeLittleEndianInt32: _centralDirectoryDisk];
	[_stream writeLittleEndianInt64: _centralDirectoryEntriesInDisk];
	[_stream writeLittleEndianInt64: _centralDirectoryEntries];
	if (_centralDirectorySize > UINT32_MAX)
		@throw [OFOutOfRangeException exception];
	[_stream writeLittleEndianInt64: _centralDirectorySize];
	[_stream writeLittleEndianInt64: _centralDirectoryOffset];

	/* ZIP64 end of central directory locator */
	[_stream writeLittleEndianInt32: 0x07064B50];
	[_stream writeLittleEndianInt32: _diskNumber];
	[_stream writeLittleEndianInt64:
	    _centralDirectoryOffset + _centralDirectorySize];
	[_stream writeLittleEndianInt32: 1];	/* Total number of disks */

	/* End of central directory */
	[_stream writeLittleEndianInt32: 0x06054B50];
	[_stream writeLittleEndianInt16: _diskNumber];
	[_stream writeLittleEndianInt16: _centralDirectoryDisk];
	[_stream writeLittleEndianInt16: _centralDirectoryEntriesInDisk];
	[_stream writeLittleEndianInt16: _centralDirectoryEntries];
	[_stream writeLittleEndianInt32: (uint32_t)_centralDirectorySize];
	[_stream writeLittleEndianInt32: (uint32_t)_centralDirectoryOffset];
	[_stream writeLittleEndianInt16: 0xFFFF];	/* Disk number */
	[_stream writeLittleEndianInt16: 0xFFFF];	/* CD disk */
	[_stream writeLittleEndianInt16: 0xFFFF];	/* CD entries in disk */
	[_stream writeLittleEndianInt16: 0xFFFF];	/* CD entries */
	[_stream writeLittleEndianInt32: 0xFFFFFFFF];	/* CD size */
	[_stream writeLittleEndianInt32: 0xFFFFFFFF];	/* CD offset */
	[_stream writeLittleEndianInt16: [_archiveComment UTF8StringLength]];
	if (_archiveComment != nil)
		[_stream writeString: _archiveComment];

	objc_autoreleasePoolPop(pool);
}

833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853


854
855
856
857
858
859
860
861


862
863



864
853
854
855
856
857
858
859


860
861
862





863
864


865
866

867
868
869
870
871


872
873
874

875
876
877
878







-
-



-
-
-
-
-


-
-
+
+
-





-
-
+
+

-
+
+
+


	_bytesWritten += (int64_t)length;
	_CRC32 = of_crc32(_CRC32, buffer, length);
}

- (void)close
{
	uint32_t bytesWritten;

	if (_stream == nil)
		return;

	if (_bytesWritten > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	bytesWritten = (uint32_t)_bytesWritten;

	[_stream writeLittleEndianInt32: 0x08074B50];
	[_stream writeLittleEndianInt32: _CRC32];
	[_stream writeLittleEndianInt32: bytesWritten];
	[_stream writeLittleEndianInt32: bytesWritten];
	[_stream writeLittleEndianInt64: _bytesWritten];
	[_stream writeLittleEndianInt64: _bytesWritten];
	_bytesWritten += (4 * 4);

	[_stream release];
	_stream = nil;

	[_entry setCRC32: ~_CRC32];
	[_entry setCompressedSize: bytesWritten];
	[_entry setUncompressedSize: bytesWritten];
	[_entry setCompressedSize: _bytesWritten];
	[_entry setUncompressedSize: _bytesWritten];
	[_entry makeImmutable];
}

	_bytesWritten += (2 * 4 + 2 * 8);
}
@end

Modified src/OFZIPArchiveEntry.m from [1983c233dd] to [fef61c95a7].

420
421
422
423
424
425
426
427

428
429
430

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446


447
448

449
450
451

452
453
454

455
456
457










458
459


460
461
462
463
464

465
466
467
468
469
470
420
421
422
423
424
425
426

427
428
429

430


431
432


433
434
435
436
437
438
439
440


441
442
443

444
445
446

447
448
449

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469



470
471
472
473
474
475
476







-
+


-
+
-
-


-
-








-
-
+
+

-
+


-
+


-
+



+
+
+
+
+
+
+
+
+
+


+
+


-
-
-
+






	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (uint64_t)of_writeToStream: (OFStream *)stream
{
	void *pool;
	void *pool = objc_autoreleasePoolPush();
	uint64_t size = 0;

	if (_compressedSize > UINT32_MAX ||
	if (UINT16_MAX - [_extraField count] < 32)
	    _uncompressedSize > UINT32_MAX ||
	    _localFileHeaderOffset > UINT32_MAX)
		@throw [OFOutOfRangeException exception];

	pool = objc_autoreleasePoolPush();

	[stream writeLittleEndianInt32: 0x02014B50];
	[stream writeLittleEndianInt16: _versionMadeBy];
	[stream writeLittleEndianInt16: _minVersionNeeded];
	[stream writeLittleEndianInt16: _generalPurposeBitFlag];
	[stream writeLittleEndianInt16: _compressionMethod];
	[stream writeLittleEndianInt16: _lastModifiedFileTime];
	[stream writeLittleEndianInt16: _lastModifiedFileDate];
	[stream writeLittleEndianInt32: _CRC32];
	[stream writeLittleEndianInt32: (uint32_t)_compressedSize];
	[stream writeLittleEndianInt32: (uint32_t)_uncompressedSize];
	[stream writeLittleEndianInt32: 0xFFFFFFFF];
	[stream writeLittleEndianInt32: 0xFFFFFFFF];
	[stream writeLittleEndianInt16: (uint16_t)[_fileName UTF8StringLength]];
	[stream writeLittleEndianInt16: (uint16_t)[_extraField count]];
	[stream writeLittleEndianInt16: (uint16_t)[_extraField count] + 32];
	[stream writeLittleEndianInt16:
	    (uint16_t)[_fileComment UTF8StringLength]];
	[stream writeLittleEndianInt16: _startDiskNumber];
	[stream writeLittleEndianInt16: 0xFFFF];
	[stream writeLittleEndianInt16: _internalAttributes];
	[stream writeLittleEndianInt32: _versionSpecificAttributes];
	[stream writeLittleEndianInt32: (uint32_t)_localFileHeaderOffset];
	[stream writeLittleEndianInt32: 0xFFFFFFFF];
	size += (4 + (6 * 2) + (3 * 4) + (5 * 2) + (2 * 4));

	[stream writeString: _fileName];
	size += (uint64_t)[_fileName UTF8StringLength];

	[stream writeLittleEndianInt16: OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64];
	[stream writeLittleEndianInt16: 28];
	[stream writeLittleEndianInt64: _uncompressedSize];
	[stream writeLittleEndianInt64: _compressedSize];
	[stream writeLittleEndianInt64: _localFileHeaderOffset];
	[stream writeLittleEndianInt32: _startDiskNumber];
	size += (2 * 2) + (3 * 8) + 4;

	if (_extraField != nil)
		[stream writeData: _extraField];
	size += (uint64_t)[_extraField count];

	if (_fileComment != nil)
		[stream writeString: _fileComment];
	size += (uint64_t)[_fileName UTF8StringLength] +
	    (uint64_t)[_extraField count] +
	    (uint64_t)[_fileComment UTF8StringLength];
	size += (uint64_t)[_fileComment UTF8StringLength];

	objc_autoreleasePoolPop(pool);

	return size;
}
@end