diff --git a/lib/Open/directory.js b/lib/Open/directory.js index 20b9e5f..b45059e 100644 --- a/lib/Open/directory.js +++ b/lib/Open/directory.js @@ -121,7 +121,7 @@ module.exports = function centralDirectory(source, options) { // https://github.com/golang/go/blob/master/src/archive/zip/reader.go#L503 // For zip64 files, need to find zip64 central directory locator header to extract // relative offset for zip64 central directory record. - if (vars.numberOfRecords == 0xffff|| vars.numberOfRecords == 0xffff || + if (vars.diskNumber == 0xffff || vars.numberOfRecords == 0xffff || vars.offsetToStartOfCentralDirectory == 0xffffffff) { // Offset to zip64 CDL is 20 bytes before normal CDR diff --git a/lib/parseExtraField.js b/lib/parseExtraField.js index 42d178f..683709c 100644 --- a/lib/parseExtraField.js +++ b/lib/parseExtraField.js @@ -6,19 +6,22 @@ module.exports = function(extraField, vars) { while(!extra && extraField && extraField.length) { const candidateExtra = parseBuffer.parse(extraField, [ ['signature', 2], - ['partsize', 2], - ['uncompressedSize', 8], - ['compressedSize', 8], - ['offset', 8], - ['disknum', 8], + ['partSize', 2], ]); if(candidateExtra.signature === 0x0001) { - extra = candidateExtra; + // parse buffer based on data in ZIP64 central directory; order is important! + const fieldsToExpect = []; + if (vars.uncompressedSize === 0xffffffff) fieldsToExpect.push(['uncompressedSize', 8]); + if (vars.compressedSize === 0xffffffff) fieldsToExpect.push(['compressedSize', 8]); + if (vars.offsetToLocalFileHeader === 0xffffffff) fieldsToExpect.push(['offsetToLocalFileHeader', 8]); + + // slice off the 4 bytes for signature and partSize + extra = parseBuffer.parse(extraField.slice(4), fieldsToExpect); } else { // Advance the buffer to the next part. // The total size of this part is the 4 byte header + partsize. - extraField = extraField.slice(candidateExtra.partsize + 4); + extraField = extraField.slice(candidateExtra.partSize + 4); } } @@ -31,7 +34,7 @@ module.exports = function(extraField, vars) { vars.uncompressedSize= extra.uncompressedSize; if (vars.offsetToLocalFileHeader === 0xffffffff) - vars.offsetToLocalFileHeader= extra.offset; + vars.offsetToLocalFileHeader = extra.offsetToLocalFileHeader; return extra; }; diff --git a/test/zip64.js b/test/zip64.js index bd34ca0..64fbb63 100644 --- a/test/zip64.js +++ b/test/zip64.js @@ -9,6 +9,7 @@ const temp = require('temp'); const UNCOMPRESSED_SIZE = 5368709120; const ZIP64_OFFSET = 72; const ZIP64_SIZE = 36; +const ZIP64_EXTRA_FIELD_OFFSET = 0; t.test('Correct uncompressed size for zip64', function (t) { const archive = path.join(__dirname, '../testData/big.zip'); @@ -43,7 +44,77 @@ t.test('Correct uncompressed size for zip64', function (t) { t.end(); }); -t.test('Parse files from zip64 format correctly', function (t) { +t.test('Parse files with all zip64 extra fields correctly', function (t) { + const archive = path.join(__dirname, '../testData/zip64-allextrafields.zip'); + + t.test('in unzipper.Open', function(t) { + unzip.Open.file(archive) + .then(function(d) { + d.files[0].stream() + .on('vars', function(vars) { + t.same(vars.extra.uncompressedSize, ZIP64_SIZE, 'Open: Extra field'); + t.same(vars.extra.compressedSize, ZIP64_SIZE, 'Open: Extra field'); + t.same(vars.extra.offsetToLocalFileHeader, ZIP64_EXTRA_FIELD_OFFSET, 'Open: Extra field'); + t.same(vars.uncompressedSize, ZIP64_SIZE, 'Open: File header'); + t.same(vars.compressedSize, ZIP64_SIZE, 'Open: File header'); + t.same(vars.offsetToLocalFileHeader, ZIP64_EXTRA_FIELD_OFFSET, 'Open: File header'); + t.end(); + }) + .on('error', function(e) { + t.same(e.message, 'FILE_ENDED'); + t.end(); + }); + }); + }); + + t.test('in unzipper.extract', function (t) { + temp.mkdir('node-unzip-', function (err, dirPath) { + if (err) { + throw err; + } + fs.createReadStream(archive) + .pipe(unzip.Extract({ path: dirPath })) + .on('close', function() { t.end(); }); + }); + }); + + t.end(); +}); + +t.test('Parse files with zip64 extra field with only offset length correctly', function (t) { + const archive = path.join(__dirname, '../testData/zip64-extrafieldoffsetlength.zip'); + + t.test('in unzipper.Open', function(t) { + unzip.Open.file(archive) + .then(function(d) { + d.files[0].stream() + .on('vars', function(vars) { + t.same(vars.extra.offsetToLocalFileHeader, ZIP64_EXTRA_FIELD_OFFSET, 'Open: Extra field'); + t.same(vars.offsetToLocalFileHeader, ZIP64_EXTRA_FIELD_OFFSET, 'Open: File header'); + t.end(); + }) + .on('error', function(e) { + t.same(e.message, 'FILE_ENDED'); + t.end(); + }); + }); + }); + + t.test('in unzipper.extract', function (t) { + temp.mkdir('node-unzip-', function (err, dirPath) { + if (err) { + throw err; + } + fs.createReadStream(archive) + .pipe(unzip.Extract({ path: dirPath })) + .on('close', function() { t.end(); }); + }); + }); + + t.end(); +}); + +t.test('Parse files from regular zip64 format correctly', function (t) { const archive = path.join(__dirname, '../testData/zip64.zip'); t.test('in unzipper.Open', function(t) { diff --git a/testData/zip64-allextrafields.zip b/testData/zip64-allextrafields.zip new file mode 100644 index 0000000..34f6884 Binary files /dev/null and b/testData/zip64-allextrafields.zip differ diff --git a/testData/zip64-extrafieldoffsetlength.zip b/testData/zip64-extrafieldoffsetlength.zip new file mode 100644 index 0000000..eeb410e Binary files /dev/null and b/testData/zip64-extrafieldoffsetlength.zip differ