@zip.js/zip.js 2.8.26 → 2.8.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/deno.json +1 -1
- package/dist/zip-core.js +1050 -569
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +1146 -617
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +1207 -627
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +1279 -681
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +1051 -570
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +1053 -572
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +1125 -626
- package/dist/zip.min.js +1 -1
- package/eslint.config.mjs +6 -1
- package/index-native.cjs +1207 -627
- package/index-native.min.js +1 -1
- package/index.cjs +1279 -681
- package/index.d.ts +2451 -2355
- package/index.min.js +1 -1
- package/lib/core/codec-pool.js +39 -1
- package/lib/core/codec-worker.js +120 -34
- package/lib/core/configuration.js +3 -0
- package/lib/core/constants.js +6 -0
- package/lib/core/io.js +55 -25
- package/lib/core/options.js +2 -0
- package/lib/core/streams/aes-crypto-stream.js +12 -12
- package/lib/core/streams/codec-stream.js +11 -5
- package/lib/core/streams/codecs/sjcl.js +1 -33
- package/lib/core/streams/common-crypto.js +4 -4
- package/lib/core/streams/zip-crypto-stream.js +25 -15
- package/lib/core/streams/zip-entry-stream.js +35 -1
- package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
- package/lib/core/streams/zlib-wasm/zlib-streams.js +73 -55
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/util/decode-cp437.js +1 -1
- package/lib/core/util/decode-text.js +2 -1
- package/lib/core/web-worker-base.js +12 -4
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +154 -55
- package/lib/core/zip-reader.js +185 -67
- package/lib/core/zip-writer.js +613 -376
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-reader.js +3 -1
- package/lib/zip-core-writer.js +1 -0
- package/package.json +20 -6
package/lib/core/zip-writer.js
CHANGED
|
@@ -26,9 +26,56 @@
|
|
|
26
26
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
/* global TransformStream */
|
|
29
|
+
/* global TransformStream, WritableStream */
|
|
30
30
|
// deno-lint-ignore-file no-this-alias
|
|
31
31
|
|
|
32
|
+
/*
|
|
33
|
+
* Internal state invariants
|
|
34
|
+
*
|
|
35
|
+
* Positions:
|
|
36
|
+
* - `zipWriter.offset` is the logical write position: the offset where the next entry will start
|
|
37
|
+
* in the final archive. It includes `zipWriter.initialOffset` (see below) and is only updated
|
|
38
|
+
* between entries, or by the recovery code in getFileEntry() when a write fails mid-entry.
|
|
39
|
+
* - `writer.size` counts the bytes actually written into the writer. Between entries,
|
|
40
|
+
* `zipWriter.offset - writer.size` is constant and equals `initialOffset`.
|
|
41
|
+
* - `zipWriter.initialOffset` is the number of bytes assumed to be prepended to the final archive
|
|
42
|
+
* (i.e. to the first segment when the archive is split): the `offset` option minus the data
|
|
43
|
+
* already stored in the writer. Positions within the first segment include it; positions within
|
|
44
|
+
* the other segments do not, see getSegmentOffset().
|
|
45
|
+
* - For split archives, `writer.diskOffset` is the total size of the closed segments and
|
|
46
|
+
* `writer.diskNumber` the index of the current segment. `writer.availableSize` is the remaining
|
|
47
|
+
* capacity of the current segment (Infinity otherwise); setting it to 0 and writing an empty
|
|
48
|
+
* chunk starts a new segment. This is how header records are kept whole within a segment, as
|
|
49
|
+
* required by APPNOTE section 8.5.2.
|
|
50
|
+
* - The segment coordinates of an entry (`diskNumberStart` and `offset`) must be read after
|
|
51
|
+
* calling skipDiskIfNeeded() and while holding the writer lock; they are unstable otherwise.
|
|
52
|
+
*
|
|
53
|
+
* Scheduling:
|
|
54
|
+
* - The central directory order (i.e. the order in which entries are listed by getEntries()) is
|
|
55
|
+
* the insertion order of the `files` map. addFile() reserves each entry's slot synchronously in
|
|
56
|
+
* add() call order, before initializing the reader, so concurrent entries whose readers
|
|
57
|
+
* initialize at different speeds are still listed in call order.
|
|
58
|
+
* - `fileEntry.lock` chains every entry to the previous one to reach getFileEntry(); awaiting it
|
|
59
|
+
* serializes the physical writes when `keepOrder` is set. The physical layout therefore follows
|
|
60
|
+
* the order in which the readers finish initializing, not necessarily the central directory
|
|
61
|
+
* order; each entry records its own `offset`, so the archive stays consistent either way.
|
|
62
|
+
* - `zipWriter.lockWriter`/`writerLocked` is the mutex over the writer; only the holder may write
|
|
63
|
+
* into it or read the position variables above.
|
|
64
|
+
* - An entry writes directly into the writer (i.e. without being buffered) only when the writer
|
|
65
|
+
* is unlocked and no buffered entry is in flight (`bufferedWrites` == 0). This avoids a
|
|
66
|
+
* deadlock, it is not an optimization: an entry written directly acquires the writer lock
|
|
67
|
+
* before awaiting the lock of the previous entry, while a buffered predecessor needs the
|
|
68
|
+
* writer lock to be flushed.
|
|
69
|
+
* - `lastFileEntry` is the tail of the lock chain (the most recently added entry).
|
|
70
|
+
*
|
|
71
|
+
* Failures:
|
|
72
|
+
* - When a write fails mid-entry, `zipWriter.offset` advances by the number of bytes written for
|
|
73
|
+
* the entry so that the offsets of subsequent entries remain correct, and
|
|
74
|
+
* `hasCorruptedEntries` is set on the ZipWriter.
|
|
75
|
+
* - closeFile() does not update `zipWriter.offset`; the position of the central directory and of
|
|
76
|
+
* the end of central directory records are derived from it.
|
|
77
|
+
*/
|
|
78
|
+
|
|
32
79
|
import {
|
|
33
80
|
MAX_32_BITS,
|
|
34
81
|
MAX_16_BITS,
|
|
@@ -41,6 +88,7 @@ import {
|
|
|
41
88
|
LOCAL_FILE_HEADER_SIGNATURE,
|
|
42
89
|
DATA_DESCRIPTOR_RECORD_SIGNATURE,
|
|
43
90
|
CENTRAL_FILE_HEADER_SIGNATURE,
|
|
91
|
+
CENTRAL_FILE_HEADER_LENGTH,
|
|
44
92
|
END_OF_CENTRAL_DIR_SIGNATURE,
|
|
45
93
|
ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
|
|
46
94
|
ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
|
|
@@ -56,12 +104,11 @@ import {
|
|
|
56
104
|
EXTRAFIELD_TYPE_INFOZIP,
|
|
57
105
|
EXTRAFIELD_TYPE_UNIX,
|
|
58
106
|
END_OF_CENTRAL_DIR_LENGTH,
|
|
59
|
-
ZIP64_END_OF_CENTRAL_DIR_LENGTH,
|
|
60
|
-
ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH,
|
|
61
107
|
ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH,
|
|
62
108
|
BITFLAG_ENCRYPTED,
|
|
63
109
|
BITFLAG_DATA_DESCRIPTOR,
|
|
64
110
|
BITFLAG_LANG_ENCODING_FLAG,
|
|
111
|
+
BITFLAG_LEVEL,
|
|
65
112
|
BITFLAG_LEVEL_FAST_MASK,
|
|
66
113
|
BITFLAG_LEVEL_SUPER_FAST_MASK,
|
|
67
114
|
BITFLAG_LEVEL_MAX_MASK,
|
|
@@ -81,9 +128,11 @@ import {
|
|
|
81
128
|
VERSION_AES,
|
|
82
129
|
DIRECTORY_SIGNATURE,
|
|
83
130
|
HEADER_SIZE,
|
|
131
|
+
HEADER_OFFSET_VERSION,
|
|
84
132
|
HEADER_OFFSET_SIGNATURE,
|
|
85
133
|
HEADER_OFFSET_COMPRESSED_SIZE,
|
|
86
134
|
HEADER_OFFSET_UNCOMPRESSED_SIZE,
|
|
135
|
+
LOCAL_HEADER_COMMON_OFFSET,
|
|
87
136
|
MIN_DATE,
|
|
88
137
|
MAX_DATE,
|
|
89
138
|
UNDEFINED_VALUE,
|
|
@@ -169,13 +218,19 @@ const ERR_INVALID_VERSION = "Version exceeds 65535";
|
|
|
169
218
|
const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
|
|
170
219
|
const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
|
|
171
220
|
const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
|
|
221
|
+
// same message as the constant exported by zip-reader.js
|
|
222
|
+
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
223
|
+
const MIN_UNIX_TIME = -2147483648;
|
|
224
|
+
const MAX_UNIX_TIME = 2147483647;
|
|
172
225
|
const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
|
|
173
226
|
const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
|
|
227
|
+
const ERR_UNDEFINED_READER = "Undefined reader";
|
|
174
228
|
const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
|
|
175
229
|
const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
|
|
176
230
|
const ERR_INVALID_GID = "Invalid gid (must be integer 0..2^32-1)";
|
|
177
231
|
const ERR_INVALID_UNIX_MODE = "Invalid UNIX mode (must be integer 0..65535)";
|
|
178
232
|
const ERR_INVALID_UNIX_EXTRA_FIELD_TYPE = "Invalid unixExtraFieldType (must be 'infozip' or 'unix')";
|
|
233
|
+
const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
|
|
179
234
|
const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
|
|
180
235
|
const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
|
|
181
236
|
|
|
@@ -201,9 +256,10 @@ class ZipWriter {
|
|
|
201
256
|
files: new Map(),
|
|
202
257
|
filenames: new Set(),
|
|
203
258
|
offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
|
|
204
|
-
|
|
259
|
+
initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
|
|
205
260
|
pendingAddFileCalls: new Set(),
|
|
206
|
-
bufferedWrites: 0
|
|
261
|
+
bufferedWrites: 0,
|
|
262
|
+
lastFileEntry: UNDEFINED_VALUE
|
|
207
263
|
});
|
|
208
264
|
}
|
|
209
265
|
|
|
@@ -212,17 +268,18 @@ class ZipWriter {
|
|
|
212
268
|
throw new Error(ERR_ZIP_NOT_EMPTY);
|
|
213
269
|
}
|
|
214
270
|
reader = new GenericReader(reader);
|
|
271
|
+
await initStream(reader);
|
|
215
272
|
const zipReader = new ZipReader(reader.readable);
|
|
216
273
|
const entries = await zipReader.getEntries();
|
|
217
274
|
await zipReader.close();
|
|
275
|
+
await initStream(this.writer);
|
|
218
276
|
await reader.readable.pipeTo(this.writer.writable, { preventClose: true, preventAbort: true });
|
|
219
277
|
this.writer.size = this.offset = reader.size;
|
|
220
278
|
this.filenames = new Set(entries.map(entry => entry.filename));
|
|
221
279
|
this.files = new Map(entries.map(entry => {
|
|
222
280
|
const {
|
|
223
281
|
version,
|
|
224
|
-
|
|
225
|
-
lastModDate,
|
|
282
|
+
rawLastModDate,
|
|
226
283
|
lastAccessDate,
|
|
227
284
|
creationDate,
|
|
228
285
|
rawFilename,
|
|
@@ -235,6 +292,7 @@ class ZipWriter {
|
|
|
235
292
|
zip64
|
|
236
293
|
} = entry;
|
|
237
294
|
let {
|
|
295
|
+
compressionMethod,
|
|
238
296
|
rawExtraFieldZip64,
|
|
239
297
|
rawExtraFieldAES,
|
|
240
298
|
rawExtraFieldExtendedTimestamp,
|
|
@@ -247,31 +305,39 @@ class ZipWriter {
|
|
|
247
305
|
rawExtraFieldAES = rawExtraFieldAES || new Uint8Array();
|
|
248
306
|
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || new Uint8Array();
|
|
249
307
|
rawExtraFieldNTFS = rawExtraFieldNTFS || new Uint8Array();
|
|
250
|
-
rawExtraFieldUnix =
|
|
308
|
+
rawExtraFieldUnix = rawExtraFieldUnix || new Uint8Array();
|
|
251
309
|
rawExtraField = rawExtraField || new Uint8Array();
|
|
310
|
+
if (entry.extraFieldAES) {
|
|
311
|
+
compressionMethod = COMPRESSION_METHOD_AES;
|
|
312
|
+
}
|
|
252
313
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
253
|
-
const zip64UncompressedSize = zip64 && uncompressedSize
|
|
254
|
-
const zip64CompressedSize = zip64 && compressedSize
|
|
314
|
+
const zip64UncompressedSize = zip64 && uncompressedSize >= MAX_32_BITS;
|
|
315
|
+
const zip64CompressedSize = zip64 && compressedSize >= MAX_32_BITS;
|
|
316
|
+
const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
|
|
255
317
|
const {
|
|
256
318
|
headerArray,
|
|
257
319
|
headerView
|
|
258
320
|
} = getHeaderArrayData({
|
|
259
321
|
version,
|
|
260
|
-
bitFlag:
|
|
322
|
+
bitFlag: bitFlagValue,
|
|
261
323
|
compressionMethod,
|
|
262
324
|
uncompressedSize,
|
|
263
325
|
compressedSize,
|
|
264
|
-
|
|
326
|
+
rawLastModDate,
|
|
265
327
|
rawFilename,
|
|
266
328
|
zip64CompressedSize,
|
|
267
329
|
zip64UncompressedSize,
|
|
268
330
|
extraFieldLength
|
|
269
331
|
});
|
|
332
|
+
const { signature } = entry;
|
|
333
|
+
if (signature !== UNDEFINED_VALUE) {
|
|
334
|
+
setUint32(headerView, HEADER_OFFSET_SIGNATURE, signature);
|
|
335
|
+
}
|
|
270
336
|
Object.assign(entry, {
|
|
271
337
|
zip64UncompressedSize,
|
|
272
338
|
zip64CompressedSize,
|
|
273
|
-
zip64Offset: zip64 && this.offset - diskOffset
|
|
274
|
-
zip64DiskNumberStart: zip64 && diskNumber
|
|
339
|
+
zip64Offset: zip64 && this.offset - diskOffset >= MAX_32_BITS,
|
|
340
|
+
zip64DiskNumberStart: zip64 && diskNumber >= MAX_16_BITS,
|
|
275
341
|
rawExtraFieldZip64,
|
|
276
342
|
rawExtraFieldAES,
|
|
277
343
|
rawExtraFieldExtendedTimestamp,
|
|
@@ -289,6 +355,7 @@ class ZipWriter {
|
|
|
289
355
|
|
|
290
356
|
async add(name = "", reader, options = {}) {
|
|
291
357
|
const zipWriter = this;
|
|
358
|
+
options = Object.assign({}, options);
|
|
292
359
|
const {
|
|
293
360
|
pendingAddFileCalls,
|
|
294
361
|
config
|
|
@@ -299,17 +366,24 @@ class ZipWriter {
|
|
|
299
366
|
await new Promise(resolve => pendingEntries.push(resolve));
|
|
300
367
|
}
|
|
301
368
|
let promiseAddFile;
|
|
369
|
+
let nameAdded;
|
|
302
370
|
try {
|
|
303
371
|
name = name.trim();
|
|
372
|
+
if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
|
|
373
|
+
name += DIRECTORY_SIGNATURE;
|
|
374
|
+
}
|
|
304
375
|
if (zipWriter.filenames.has(name)) {
|
|
305
376
|
throw new Error(ERR_DUPLICATED_NAME);
|
|
306
377
|
}
|
|
307
378
|
zipWriter.filenames.add(name);
|
|
379
|
+
nameAdded = true;
|
|
308
380
|
promiseAddFile = addFile(zipWriter, name, reader, options);
|
|
309
381
|
pendingAddFileCalls.add(promiseAddFile);
|
|
310
382
|
return await promiseAddFile;
|
|
311
383
|
} catch (error) {
|
|
312
|
-
|
|
384
|
+
if (nameAdded) {
|
|
385
|
+
zipWriter.filenames.delete(name);
|
|
386
|
+
}
|
|
313
387
|
throw error;
|
|
314
388
|
} finally {
|
|
315
389
|
pendingAddFileCalls.delete(promiseAddFile);
|
|
@@ -342,6 +416,9 @@ class ZipWriter {
|
|
|
342
416
|
const zipWriter = this;
|
|
343
417
|
const { pendingAddFileCalls, writer } = this;
|
|
344
418
|
const { writable } = writer;
|
|
419
|
+
if (getLength(comment) > MAX_16_BITS) {
|
|
420
|
+
throw new Error(ERR_INVALID_COMMENT);
|
|
421
|
+
}
|
|
345
422
|
while (pendingAddFileCalls.size) {
|
|
346
423
|
await Promise.allSettled(Array.from(pendingAddFileCalls));
|
|
347
424
|
}
|
|
@@ -360,27 +437,62 @@ class ZipWriterStream {
|
|
|
360
437
|
const { readable, writable } = new TransformStream();
|
|
361
438
|
this.readable = readable;
|
|
362
439
|
this.zipWriter = new ZipWriter(writable, options);
|
|
440
|
+
this.pendingAddFileCalls = new Set();
|
|
363
441
|
}
|
|
364
442
|
|
|
365
443
|
transform(path) {
|
|
444
|
+
const zipWriter = this.zipWriter;
|
|
445
|
+
let streamController;
|
|
366
446
|
const { readable, writable } = new TransformStream({
|
|
367
|
-
|
|
447
|
+
start(controller) {
|
|
448
|
+
streamController = controller;
|
|
449
|
+
},
|
|
450
|
+
flush: () => void closeArchive()
|
|
368
451
|
});
|
|
369
|
-
this.zipWriter.add(path, readable);
|
|
452
|
+
watchAddFileCall(this, this.zipWriter.add(path, readable), error => streamController.error(error));
|
|
370
453
|
return { readable: this.readable, writable };
|
|
454
|
+
|
|
455
|
+
async function closeArchive() {
|
|
456
|
+
try {
|
|
457
|
+
await zipWriter.close();
|
|
458
|
+
} catch (error) {
|
|
459
|
+
try {
|
|
460
|
+
await zipWriter.writer.writable.abort(error);
|
|
461
|
+
} catch {
|
|
462
|
+
// ignored
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
371
466
|
}
|
|
372
467
|
|
|
373
468
|
writable(path) {
|
|
374
|
-
|
|
375
|
-
|
|
469
|
+
let streamController;
|
|
470
|
+
const { readable, writable } = new TransformStream({
|
|
471
|
+
start(controller) {
|
|
472
|
+
streamController = controller;
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
watchAddFileCall(this, this.zipWriter.add(path, readable), error => streamController.error(error));
|
|
376
476
|
return writable;
|
|
377
477
|
}
|
|
378
478
|
|
|
379
|
-
close(comment = UNDEFINED_VALUE, options = {}) {
|
|
479
|
+
async close(comment = UNDEFINED_VALUE, options = {}) {
|
|
480
|
+
await Promise.all(Array.from(this.pendingAddFileCalls));
|
|
380
481
|
return this.zipWriter.close(comment, options);
|
|
381
482
|
}
|
|
382
483
|
}
|
|
383
484
|
|
|
485
|
+
function watchAddFileCall(zipWriterStream, promiseAddFile, onerror) {
|
|
486
|
+
zipWriterStream.pendingAddFileCalls.add(promiseAddFile);
|
|
487
|
+
promiseAddFile.catch(error => {
|
|
488
|
+
try {
|
|
489
|
+
onerror(error);
|
|
490
|
+
} catch {
|
|
491
|
+
// ignored
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
384
496
|
export {
|
|
385
497
|
ZipWriter,
|
|
386
498
|
ZipWriterStream,
|
|
@@ -394,10 +506,42 @@ export {
|
|
|
394
506
|
ERR_INVALID_ENCRYPTION_STRENGTH,
|
|
395
507
|
ERR_UNSUPPORTED_FORMAT,
|
|
396
508
|
ERR_UNDEFINED_UNCOMPRESSED_SIZE,
|
|
509
|
+
ERR_UNDEFINED_READER,
|
|
397
510
|
ERR_ZIP_NOT_EMPTY
|
|
398
511
|
};
|
|
399
512
|
|
|
400
513
|
async function addFile(zipWriter, name, reader, options) {
|
|
514
|
+
const attributesInfo = resolveAttributes(zipWriter, name, options);
|
|
515
|
+
({ name } = attributesInfo);
|
|
516
|
+
const metadataInfo = resolveMetadata(zipWriter, name, options);
|
|
517
|
+
const { comment } = metadataInfo;
|
|
518
|
+
const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
|
|
519
|
+
zipWriter.files.set(name, UNDEFINED_VALUE);
|
|
520
|
+
let fileEntry;
|
|
521
|
+
try {
|
|
522
|
+
const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
|
|
523
|
+
({ reader } = sizesInfo);
|
|
524
|
+
const { diskOffset, diskNumber } = zipWriter.writer;
|
|
525
|
+
options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
|
|
526
|
+
internalFileAttribute: metadataInfo.resolvedOptions.internalFileAttributes,
|
|
527
|
+
externalFileAttribute: attributesInfo.resolvedOptions.externalFileAttributes,
|
|
528
|
+
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
529
|
+
offset: zipWriter.offset - diskOffset,
|
|
530
|
+
diskNumberStart: diskNumber
|
|
531
|
+
});
|
|
532
|
+
const headerInfo = getHeaderInfo(options);
|
|
533
|
+
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
534
|
+
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
535
|
+
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
zipWriter.files.delete(name);
|
|
538
|
+
throw error;
|
|
539
|
+
}
|
|
540
|
+
Object.assign(fileEntry, { name, comment, extraField });
|
|
541
|
+
return new Entry(fileEntry);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function resolveAttributes(zipWriter, name, options) {
|
|
401
545
|
name = name.trim();
|
|
402
546
|
let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
|
|
403
547
|
let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
|
|
@@ -405,7 +549,7 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
405
549
|
const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
|
|
406
550
|
const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
|
|
407
551
|
let unixMode = getOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
|
|
408
|
-
|
|
552
|
+
let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
|
|
409
553
|
let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
|
|
410
554
|
let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
|
|
411
555
|
let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
|
|
@@ -421,6 +565,13 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
421
565
|
if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
|
|
422
566
|
throw new Error(ERR_INVALID_UNIX_EXTRA_FIELD_TYPE);
|
|
423
567
|
}
|
|
568
|
+
if (unixExtraFieldType === UNIX_EXTRA_FIELD_TYPE &&
|
|
569
|
+
((uid !== UNDEFINED_VALUE && uid > MAX_16_BITS) || (gid !== UNDEFINED_VALUE && gid > MAX_16_BITS))) {
|
|
570
|
+
throw new Error(ERR_INVALID_UNIX_ID_SIZE);
|
|
571
|
+
}
|
|
572
|
+
if (unixExtraFieldType === UNDEFINED_VALUE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
573
|
+
unixExtraFieldType = INFOZIP_EXTRA_FIELD_TYPE;
|
|
574
|
+
}
|
|
424
575
|
let msdosAttributesRaw = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW);
|
|
425
576
|
let msdosAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES);
|
|
426
577
|
const hasUnixMetadata = uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE || unixMode !== UNDEFINED_VALUE || unixExtraFieldType;
|
|
@@ -491,6 +642,27 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
491
642
|
if (hasMsDosProvided) {
|
|
492
643
|
externalFileAttributes = (externalFileAttributes & MAX_32_BITS) | (msdosAttributesRaw & MAX_8_BITS);
|
|
493
644
|
}
|
|
645
|
+
return {
|
|
646
|
+
name,
|
|
647
|
+
resolvedOptions: {
|
|
648
|
+
versionMadeBy,
|
|
649
|
+
msDosCompatible,
|
|
650
|
+
externalFileAttributes,
|
|
651
|
+
unixExternalUpper,
|
|
652
|
+
uid,
|
|
653
|
+
gid,
|
|
654
|
+
unixMode,
|
|
655
|
+
unixExtraFieldType,
|
|
656
|
+
setuid,
|
|
657
|
+
setgid,
|
|
658
|
+
sticky,
|
|
659
|
+
msdosAttributesRaw,
|
|
660
|
+
msdosAttributes
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function resolveMetadata(zipWriter, name, options) {
|
|
494
666
|
const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
|
|
495
667
|
let rawFilename = encode(name);
|
|
496
668
|
if (rawFilename === UNDEFINED_VALUE) {
|
|
@@ -529,10 +701,14 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
529
701
|
const transferStreams = getOptionValue(zipWriter, options, OPTION_TRANSFER_STREAMS, true);
|
|
530
702
|
const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
|
|
531
703
|
const createTempStream = getOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
|
|
532
|
-
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE,
|
|
704
|
+
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
|
|
533
705
|
const signal = getOptionValue(zipWriter, options, OPTION_SIGNAL);
|
|
534
706
|
const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
|
|
535
707
|
const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
|
|
708
|
+
if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
|
|
709
|
+
compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE) {
|
|
710
|
+
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
711
|
+
}
|
|
536
712
|
let level = getOptionValue(zipWriter, options, OPTION_LEVEL);
|
|
537
713
|
let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
|
|
538
714
|
let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
|
|
@@ -548,7 +724,7 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
548
724
|
if (!useCompressionStream && (zipWriter.config.CompressionStream === UNDEFINED_VALUE && zipWriter.config.CompressionStreamZlib === UNDEFINED_VALUE)) {
|
|
549
725
|
level = 0;
|
|
550
726
|
}
|
|
551
|
-
|
|
727
|
+
const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
552
728
|
if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
|
|
553
729
|
throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
|
|
554
730
|
}
|
|
@@ -559,6 +735,7 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
559
735
|
let offset = 0;
|
|
560
736
|
extraField.forEach(data => extraFieldSize += 4 + getLength(data));
|
|
561
737
|
rawExtraField = new Uint8Array(extraFieldSize);
|
|
738
|
+
const rawExtraFieldView = getDataView(rawExtraField);
|
|
562
739
|
extraField.forEach((data, type) => {
|
|
563
740
|
if (type > MAX_16_BITS) {
|
|
564
741
|
throw new Error(ERR_INVALID_EXTRAFIELD_TYPE);
|
|
@@ -566,22 +743,72 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
566
743
|
if (getLength(data) > MAX_16_BITS) {
|
|
567
744
|
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
568
745
|
}
|
|
569
|
-
|
|
570
|
-
|
|
746
|
+
setUint16(rawExtraFieldView, offset, type);
|
|
747
|
+
setUint16(rawExtraFieldView, offset + 2, getLength(data));
|
|
571
748
|
arraySet(rawExtraField, data, offset + 4);
|
|
572
749
|
offset += 4 + getLength(data);
|
|
573
750
|
});
|
|
574
751
|
}
|
|
752
|
+
return {
|
|
753
|
+
comment,
|
|
754
|
+
resolvedOptions: {
|
|
755
|
+
rawFilename,
|
|
756
|
+
rawComment,
|
|
757
|
+
version,
|
|
758
|
+
lastModDate,
|
|
759
|
+
lastAccessDate,
|
|
760
|
+
creationDate,
|
|
761
|
+
internalFileAttributes,
|
|
762
|
+
passThrough,
|
|
763
|
+
password,
|
|
764
|
+
rawPassword,
|
|
765
|
+
encryptionStrength,
|
|
766
|
+
zipCrypto,
|
|
767
|
+
extendedTimestamp,
|
|
768
|
+
keepOrder,
|
|
769
|
+
useWebWorkers,
|
|
770
|
+
transferStreams,
|
|
771
|
+
bufferedWrite,
|
|
772
|
+
createTempStream,
|
|
773
|
+
dataDescriptorSignature,
|
|
774
|
+
signal,
|
|
775
|
+
useUnicodeFileNames,
|
|
776
|
+
compressionMethod,
|
|
777
|
+
level,
|
|
778
|
+
useCompressionStream,
|
|
779
|
+
dataDescriptor,
|
|
780
|
+
zip64,
|
|
781
|
+
rawExtraField
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
|
|
787
|
+
const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
|
|
788
|
+
let { dataDescriptor, zip64, level, compressionMethod } = metadata;
|
|
575
789
|
let maximumCompressedSize = 0;
|
|
576
|
-
let maximumEntrySize = 0;
|
|
577
790
|
let uncompressedSize = 0;
|
|
578
791
|
if (passThrough) {
|
|
792
|
+
// the headers of a passThrough entry describe the payload verbatim: without a
|
|
793
|
+
// reader they would declare content that is not there
|
|
794
|
+
if (!reader) {
|
|
795
|
+
throw new Error(ERR_UNDEFINED_READER);
|
|
796
|
+
}
|
|
579
797
|
uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
|
|
580
798
|
if (uncompressedSize === UNDEFINED_VALUE) {
|
|
581
799
|
throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
|
|
582
800
|
}
|
|
583
801
|
}
|
|
584
802
|
const zip64Enabled = zip64 === true;
|
|
803
|
+
const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
|
|
804
|
+
// entries without a reader have no payload: write them as empty stored files instead of
|
|
805
|
+
// declaring a compressed or encrypted content that does not exist
|
|
806
|
+
const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
|
|
807
|
+
if (!reader) {
|
|
808
|
+
level = 0;
|
|
809
|
+
compressionMethod = COMPRESSION_METHOD_STORE;
|
|
810
|
+
}
|
|
811
|
+
const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
585
812
|
if (reader) {
|
|
586
813
|
reader = new GenericReader(reader);
|
|
587
814
|
await initStream(reader);
|
|
@@ -594,16 +821,15 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
594
821
|
}
|
|
595
822
|
} else {
|
|
596
823
|
options.uncompressedSize = uncompressedSize = reader.size;
|
|
597
|
-
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
|
|
824
|
+
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
598
825
|
}
|
|
599
826
|
} else {
|
|
600
827
|
options.uncompressedSize = uncompressedSize;
|
|
601
|
-
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
|
|
828
|
+
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
602
829
|
}
|
|
603
830
|
}
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
const zip64CompressedSize = zip64Enabled || maximumCompressedSize > MAX_32_BITS;
|
|
831
|
+
const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
|
|
832
|
+
const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
|
|
607
833
|
if (zip64UncompressedSize || zip64CompressedSize) {
|
|
608
834
|
if (zip64 === false) {
|
|
609
835
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
@@ -612,73 +838,19 @@ async function addFile(zipWriter, name, reader, options) {
|
|
|
612
838
|
}
|
|
613
839
|
}
|
|
614
840
|
zip64 = zip64 || false;
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
password,
|
|
629
|
-
rawPassword,
|
|
630
|
-
level,
|
|
631
|
-
useWebWorkers,
|
|
632
|
-
transferStreams,
|
|
633
|
-
encryptionStrength,
|
|
634
|
-
extendedTimestamp,
|
|
635
|
-
zipCrypto,
|
|
636
|
-
bufferedWrite,
|
|
637
|
-
createTempStream,
|
|
638
|
-
keepOrder,
|
|
639
|
-
useUnicodeFileNames,
|
|
640
|
-
dataDescriptor,
|
|
641
|
-
dataDescriptorSignature,
|
|
642
|
-
signal,
|
|
643
|
-
msDosCompatible,
|
|
644
|
-
internalFileAttribute: internalFileAttributes,
|
|
645
|
-
internalFileAttributes,
|
|
646
|
-
externalFileAttribute: externalFileAttributes,
|
|
647
|
-
externalFileAttributes,
|
|
648
|
-
useCompressionStream,
|
|
649
|
-
passThrough,
|
|
650
|
-
encrypted: Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted),
|
|
651
|
-
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
652
|
-
compressionMethod,
|
|
653
|
-
uncompressedSize,
|
|
654
|
-
offset: zipWriter.offset - diskOffset,
|
|
655
|
-
diskNumberStart: diskNumber,
|
|
656
|
-
uid,
|
|
657
|
-
gid,
|
|
658
|
-
setuid,
|
|
659
|
-
setgid,
|
|
660
|
-
sticky,
|
|
661
|
-
unixMode,
|
|
662
|
-
msdosAttributesRaw,
|
|
663
|
-
msdosAttributes,
|
|
664
|
-
unixExternalUpper
|
|
665
|
-
});
|
|
666
|
-
const headerInfo = getHeaderInfo(options);
|
|
667
|
-
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
668
|
-
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
669
|
-
maximumEntrySize = metadataSize + maximumCompressedSize;
|
|
670
|
-
if (zipWriter.options[OPTION_USDZ]) {
|
|
671
|
-
maximumEntrySize += maximumEntrySize + 64;
|
|
672
|
-
}
|
|
673
|
-
zipWriter.pendingEntriesSize += maximumEntrySize;
|
|
674
|
-
let fileEntry;
|
|
675
|
-
try {
|
|
676
|
-
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
677
|
-
} finally {
|
|
678
|
-
zipWriter.pendingEntriesSize -= maximumEntrySize;
|
|
679
|
-
}
|
|
680
|
-
Object.assign(fileEntry, { name, comment, extraField });
|
|
681
|
-
return new Entry(fileEntry);
|
|
841
|
+
return {
|
|
842
|
+
reader,
|
|
843
|
+
resolvedOptions: {
|
|
844
|
+
dataDescriptor,
|
|
845
|
+
zip64,
|
|
846
|
+
zip64UncompressedSize,
|
|
847
|
+
zip64CompressedSize,
|
|
848
|
+
uncompressedSize,
|
|
849
|
+
level,
|
|
850
|
+
compressionMethod,
|
|
851
|
+
encrypted: encryptedEntry
|
|
852
|
+
}
|
|
853
|
+
};
|
|
682
854
|
}
|
|
683
855
|
|
|
684
856
|
async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
@@ -695,37 +867,40 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
|
695
867
|
headerInfo
|
|
696
868
|
} = entryInfo;
|
|
697
869
|
const usdz = zipWriter.options[OPTION_USDZ];
|
|
698
|
-
const previousFileEntry =
|
|
870
|
+
const previousFileEntry = zipWriter.lastFileEntry;
|
|
699
871
|
let fileEntry = {};
|
|
700
872
|
let bufferedWrite;
|
|
701
873
|
let releaseLockWriter;
|
|
702
874
|
let releaseLockCurrentFileEntry;
|
|
703
875
|
let writingBufferedEntryData;
|
|
704
876
|
let writingEntryData;
|
|
877
|
+
let writerSizeBeforeEntry;
|
|
878
|
+
let flushedBufferedSize = 0;
|
|
705
879
|
let fileWriter;
|
|
706
880
|
files.set(name, fileEntry);
|
|
881
|
+
zipWriter.lastFileEntry = fileEntry;
|
|
707
882
|
try {
|
|
708
883
|
let lockPreviousFileEntry;
|
|
709
884
|
if (keepOrder) {
|
|
710
885
|
lockPreviousFileEntry = previousFileEntry && previousFileEntry.lock;
|
|
711
886
|
requestLockCurrentFileEntry();
|
|
712
887
|
}
|
|
713
|
-
if (
|
|
888
|
+
if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || !dataDescriptor) {
|
|
889
|
+
bufferedWrite = true;
|
|
890
|
+
zipWriter.bufferedWrites++;
|
|
714
891
|
if (options.createTempStream) {
|
|
715
892
|
fileWriter = await options.createTempStream();
|
|
716
893
|
} else {
|
|
717
894
|
fileWriter = new TransformStream(UNDEFINED_VALUE, UNDEFINED_VALUE, { highWaterMark: INFINITY_VALUE });
|
|
718
895
|
}
|
|
719
896
|
fileWriter.size = 0;
|
|
720
|
-
bufferedWrite = true;
|
|
721
|
-
zipWriter.bufferedWrites++;
|
|
722
897
|
await initStream(writer);
|
|
723
898
|
} else {
|
|
724
899
|
fileWriter = writer;
|
|
725
900
|
await requestLockWriter();
|
|
726
901
|
}
|
|
727
902
|
await initStream(fileWriter);
|
|
728
|
-
const {
|
|
903
|
+
const { diskOffset } = writer;
|
|
729
904
|
if (zipWriter.addSplitZipSignature) {
|
|
730
905
|
delete zipWriter.addSplitZipSignature;
|
|
731
906
|
const signatureArray = new Uint8Array(4);
|
|
@@ -734,21 +909,20 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
|
734
909
|
await writeData(writer, signatureArray);
|
|
735
910
|
zipWriter.offset += 4;
|
|
736
911
|
}
|
|
737
|
-
if (usdz) {
|
|
912
|
+
if (usdz && !bufferedWrite) {
|
|
738
913
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
|
|
739
914
|
}
|
|
740
|
-
const {
|
|
741
|
-
localHeaderView,
|
|
742
|
-
localHeaderArray
|
|
743
|
-
} = headerInfo;
|
|
915
|
+
const { localHeaderArray } = headerInfo;
|
|
744
916
|
if (!bufferedWrite) {
|
|
745
917
|
await lockPreviousFileEntry;
|
|
746
|
-
await skipDiskIfNeeded(
|
|
918
|
+
await skipDiskIfNeeded();
|
|
747
919
|
}
|
|
748
|
-
const
|
|
749
|
-
|
|
920
|
+
const diskNumberStart = writer.diskNumber;
|
|
921
|
+
const entryOffset = getSegmentOffset(zipWriter, writer);
|
|
922
|
+
fileEntry.diskNumberStart = diskNumberStart;
|
|
750
923
|
if (!bufferedWrite) {
|
|
751
924
|
writingEntryData = true;
|
|
925
|
+
writerSizeBeforeEntry = writer.size;
|
|
752
926
|
await writeData(fileWriter, localHeaderArray);
|
|
753
927
|
}
|
|
754
928
|
fileEntry = await createFileEntry(reader, fileWriter, fileEntry, entryInfo, zipWriter.config, options);
|
|
@@ -761,16 +935,24 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
|
761
935
|
await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
|
|
762
936
|
await requestLockWriter();
|
|
763
937
|
writingBufferedEntryData = true;
|
|
938
|
+
writerSizeBeforeEntry = writer.size;
|
|
939
|
+
await skipDiskIfNeeded();
|
|
764
940
|
fileEntry.diskNumberStart = writer.diskNumber;
|
|
765
|
-
fileEntry.offset = zipWriter
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
941
|
+
fileEntry.offset = getSegmentOffset(zipWriter, writer);
|
|
942
|
+
if (usdz) {
|
|
943
|
+
// the padding depends on the entry offset, only known now that the writer is locked
|
|
944
|
+
const previousMetadataSize = entryInfo.metadataSize;
|
|
945
|
+
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - writer.diskOffset);
|
|
946
|
+
fileEntry.size += entryInfo.metadataSize - previousMetadataSize;
|
|
947
|
+
}
|
|
948
|
+
updateLocalHeader(fileEntry, headerInfo.localHeaderView, options);
|
|
949
|
+
await writeData(writer, headerInfo.localHeaderArray);
|
|
950
|
+
await flushBufferedData(fileWriter.readable, writer, signal, chunkLength => flushedBufferedSize += chunkLength);
|
|
770
951
|
writer.size += fileWriter.size;
|
|
771
952
|
writingBufferedEntryData = false;
|
|
772
953
|
} else {
|
|
773
|
-
fileEntry.
|
|
954
|
+
fileEntry.diskNumberStart = diskNumberStart;
|
|
955
|
+
fileEntry.offset = entryOffset;
|
|
774
956
|
}
|
|
775
957
|
zipWriter.offset += fileEntry.size;
|
|
776
958
|
return fileEntry;
|
|
@@ -784,10 +966,9 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
|
784
966
|
// ignored
|
|
785
967
|
}
|
|
786
968
|
}
|
|
969
|
+
zipWriter.offset += writer.size - writerSizeBeforeEntry;
|
|
787
970
|
if (bufferedWrite) {
|
|
788
|
-
zipWriter.offset +=
|
|
789
|
-
} else {
|
|
790
|
-
zipWriter.offset = fileWriter.size;
|
|
971
|
+
zipWriter.offset += flushedBufferedSize;
|
|
791
972
|
}
|
|
792
973
|
}
|
|
793
974
|
files.delete(name);
|
|
@@ -818,10 +999,10 @@ async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
|
818
999
|
await lockWriter;
|
|
819
1000
|
}
|
|
820
1001
|
|
|
821
|
-
async function skipDiskIfNeeded(
|
|
1002
|
+
async function skipDiskIfNeeded() {
|
|
822
1003
|
if (getLength(headerInfo.localHeaderArray) > writer.availableSize) {
|
|
823
1004
|
writer.availableSize = 0;
|
|
824
|
-
await writeData(
|
|
1005
|
+
await writeData(writer, new Uint8Array());
|
|
825
1006
|
}
|
|
826
1007
|
}
|
|
827
1008
|
}
|
|
@@ -962,6 +1143,10 @@ async function createFileEntry(reader, writer, { diskNumberStart, lock }, entryI
|
|
|
962
1143
|
uncompressedSize = result.inputSize;
|
|
963
1144
|
signature = result.signature;
|
|
964
1145
|
}
|
|
1146
|
+
if ((!zip64CompressedSize && compressedSize >= MAX_32_BITS) ||
|
|
1147
|
+
(!zip64UncompressedSize && uncompressedSize >= MAX_32_BITS)) {
|
|
1148
|
+
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
1149
|
+
}
|
|
965
1150
|
} catch (error) {
|
|
966
1151
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
967
1152
|
writer.size += error.outputSize;
|
|
@@ -1024,35 +1209,28 @@ function getHeaderInfo(options) {
|
|
|
1024
1209
|
uncompressedSize
|
|
1025
1210
|
} = options;
|
|
1026
1211
|
let { version, compressionMethod } = options;
|
|
1027
|
-
const compressed = !directory && (
|
|
1212
|
+
const compressed = !directory && (compressionMethod === UNDEFINED_VALUE
|
|
1213
|
+
? (level === UNDEFINED_VALUE || level > 0)
|
|
1214
|
+
: compressionMethod !== COMPRESSION_METHOD_STORE);
|
|
1028
1215
|
let rawLocalExtraFieldZip64;
|
|
1029
1216
|
const uncompressedFile = passThrough || !compressed;
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
setUint16(rawLocalExtraFieldZip64View, 2, getLength(rawLocalExtraFieldZip64) - 4);
|
|
1217
|
+
// entries without a data descriptor are always buffered (cf. getFileEntry), so the
|
|
1218
|
+
// zip64 extra field values can be patched into the local header before flushing it
|
|
1219
|
+
const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
|
|
1220
|
+
const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
|
|
1221
|
+
if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
|
|
1222
|
+
// APPNOTE 4.5.3: the zip64 extra field of a local header must include both the
|
|
1223
|
+
// uncompressed and compressed size fields
|
|
1224
|
+
const length = 4 + 16;
|
|
1225
|
+
const extraFieldZip64 = createRecordWriter(length);
|
|
1226
|
+
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
1227
|
+
extraFieldZip64.uint16(length - 4);
|
|
1228
|
+
rawLocalExtraFieldZip64 = extraFieldZip64.array;
|
|
1043
1229
|
if (zip64ExtraFieldComplete) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
rawLocalExtraFieldZip64Offset += 8;
|
|
1049
|
-
}
|
|
1050
|
-
if (zip64CompressedSize && uncompressedFile) {
|
|
1051
|
-
setBigUint64(rawLocalExtraFieldZip64View, rawLocalExtraFieldZip64Offset, BigInt(uncompressedSize));
|
|
1052
|
-
rawLocalExtraFieldZip64Offset += 8;
|
|
1053
|
-
}
|
|
1054
|
-
if (rawLocalExtraFieldZip64Offset == 4) {
|
|
1055
|
-
rawLocalExtraFieldZip64 = new Uint8Array();
|
|
1230
|
+
extraFieldZip64.uint64(uncompressedSize);
|
|
1231
|
+
if (uncompressedFile) {
|
|
1232
|
+
const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
1233
|
+
extraFieldZip64.uint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
|
|
1056
1234
|
}
|
|
1057
1235
|
}
|
|
1058
1236
|
} else {
|
|
@@ -1060,11 +1238,11 @@ function getHeaderInfo(options) {
|
|
|
1060
1238
|
}
|
|
1061
1239
|
let rawExtraFieldAES;
|
|
1062
1240
|
if (encrypted && !zipCrypto) {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1241
|
+
const extraFieldAES = createRecordWriter(getLength(EXTRAFIELD_DATA_AES) + 2);
|
|
1242
|
+
extraFieldAES.uint16(EXTRAFIELD_TYPE_AES);
|
|
1243
|
+
extraFieldAES.bytes(EXTRAFIELD_DATA_AES);
|
|
1244
|
+
rawExtraFieldAES = extraFieldAES.array;
|
|
1245
|
+
rawExtraFieldAES[8] = encryptionStrength;
|
|
1068
1246
|
} else {
|
|
1069
1247
|
rawExtraFieldAES = new Uint8Array();
|
|
1070
1248
|
}
|
|
@@ -1072,33 +1250,38 @@ function getHeaderInfo(options) {
|
|
|
1072
1250
|
let rawExtraFieldExtendedTimestamp;
|
|
1073
1251
|
let extraFieldExtendedTimestampFlag;
|
|
1074
1252
|
if (extendedTimestamp) {
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1253
|
+
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
1254
|
+
if (inUnixTimeRange(lastModTimeUnix)) {
|
|
1255
|
+
const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
|
|
1256
|
+
const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
|
|
1257
|
+
extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
|
|
1258
|
+
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
1259
|
+
extraFieldTimestamp.uint16(extraFieldTimestampLength - 4);
|
|
1260
|
+
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
1261
|
+
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
1262
|
+
if (lastAccessDate) {
|
|
1263
|
+
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(lastAccessDate)));
|
|
1264
|
+
}
|
|
1265
|
+
if (creationDate) {
|
|
1266
|
+
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(creationDate)));
|
|
1267
|
+
}
|
|
1268
|
+
rawExtraFieldExtendedTimestamp = extraFieldTimestamp.array;
|
|
1269
|
+
} else {
|
|
1270
|
+
// the date cannot be stored as a 32-bit time_t; the NTFS field replaces it
|
|
1271
|
+
rawExtraFieldExtendedTimestamp = new Uint8Array();
|
|
1090
1272
|
}
|
|
1091
1273
|
try {
|
|
1092
|
-
rawExtraFieldNTFS = new Uint8Array(36);
|
|
1093
|
-
const extraFieldNTFSView = getDataView(rawExtraFieldNTFS);
|
|
1094
1274
|
const lastModTimeNTFS = getTimeNTFS(lastModDate);
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1275
|
+
const extraFieldNTFS = createRecordWriter(36);
|
|
1276
|
+
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS);
|
|
1277
|
+
extraFieldNTFS.uint16(32);
|
|
1278
|
+
extraFieldNTFS.skip(4);
|
|
1279
|
+
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS_TAG1);
|
|
1280
|
+
extraFieldNTFS.uint16(24);
|
|
1281
|
+
extraFieldNTFS.uint64(lastModTimeNTFS);
|
|
1282
|
+
extraFieldNTFS.uint64(getTimeNTFS(lastAccessDate) || lastModTimeNTFS);
|
|
1283
|
+
extraFieldNTFS.uint64(getTimeNTFS(creationDate) || lastModTimeNTFS);
|
|
1284
|
+
rawExtraFieldNTFS = extraFieldNTFS.array;
|
|
1102
1285
|
} catch {
|
|
1103
1286
|
rawExtraFieldNTFS = new Uint8Array();
|
|
1104
1287
|
}
|
|
@@ -1107,41 +1290,27 @@ function getHeaderInfo(options) {
|
|
|
1107
1290
|
}
|
|
1108
1291
|
let rawExtraFieldUnix;
|
|
1109
1292
|
try {
|
|
1110
|
-
const { uid, gid,
|
|
1111
|
-
if (unixExtraFieldType && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE
|
|
1293
|
+
const { uid, gid, unixExtraFieldType } = options;
|
|
1294
|
+
if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
1112
1295
|
const uidBytes = packUnixId(uid);
|
|
1113
1296
|
const gidBytes = packUnixId(gid);
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
rawExtraFieldUnix = new Uint8Array(4 + payloadLength);
|
|
1132
|
-
const rawExtraFieldUnixView = getDataView(rawExtraFieldUnix);
|
|
1133
|
-
setUint16(rawExtraFieldUnixView, 0, unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE ? EXTRAFIELD_TYPE_INFOZIP : EXTRAFIELD_TYPE_UNIX);
|
|
1134
|
-
setUint16(rawExtraFieldUnixView, 2, payloadLength);
|
|
1135
|
-
setUint8(rawExtraFieldUnixView, 4, 1);
|
|
1136
|
-
setUint8(rawExtraFieldUnixView, 5, uidBytes.length);
|
|
1137
|
-
let offset = 6;
|
|
1138
|
-
arraySet(rawExtraFieldUnix, uidBytes, offset);
|
|
1139
|
-
offset += uidBytes.length;
|
|
1140
|
-
setUint8(rawExtraFieldUnixView, offset, gidBytes.length);
|
|
1141
|
-
offset++;
|
|
1142
|
-
arraySet(rawExtraFieldUnix, gidBytes, offset);
|
|
1143
|
-
offset += gidBytes.length;
|
|
1144
|
-
arraySet(rawExtraFieldUnix, modeArray, offset);
|
|
1297
|
+
const payloadLength = 3 + uidBytes.length + gidBytes.length;
|
|
1298
|
+
const extraFieldUnix = createRecordWriter(4 + payloadLength);
|
|
1299
|
+
extraFieldUnix.uint16(EXTRAFIELD_TYPE_INFOZIP);
|
|
1300
|
+
extraFieldUnix.uint16(payloadLength);
|
|
1301
|
+
extraFieldUnix.uint8(1);
|
|
1302
|
+
extraFieldUnix.uint8(uidBytes.length);
|
|
1303
|
+
extraFieldUnix.bytes(uidBytes);
|
|
1304
|
+
extraFieldUnix.uint8(gidBytes.length);
|
|
1305
|
+
extraFieldUnix.bytes(gidBytes);
|
|
1306
|
+
rawExtraFieldUnix = extraFieldUnix.array;
|
|
1307
|
+
} else if (unixExtraFieldType == UNIX_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
1308
|
+
const extraFieldUnix = createRecordWriter(8);
|
|
1309
|
+
extraFieldUnix.uint16(EXTRAFIELD_TYPE_UNIX);
|
|
1310
|
+
extraFieldUnix.uint16(4);
|
|
1311
|
+
extraFieldUnix.uint16((uid === UNDEFINED_VALUE ? 0 : uid) & MAX_16_BITS);
|
|
1312
|
+
extraFieldUnix.uint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
|
|
1313
|
+
rawExtraFieldUnix = extraFieldUnix.array;
|
|
1145
1314
|
} else {
|
|
1146
1315
|
rawExtraFieldUnix = new Uint8Array();
|
|
1147
1316
|
}
|
|
@@ -1159,8 +1328,11 @@ function getHeaderInfo(options) {
|
|
|
1159
1328
|
rawExtraFieldAES[9] = compressionMethod;
|
|
1160
1329
|
compressionMethod = COMPRESSION_METHOD_AES;
|
|
1161
1330
|
}
|
|
1162
|
-
const localExtraFieldZip64Length =
|
|
1331
|
+
const localExtraFieldZip64Length = writeLocalExtraFieldZip64 ? getLength(rawLocalExtraFieldZip64) : 0;
|
|
1163
1332
|
const extraFieldLength = localExtraFieldZip64Length + getLength(rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
1333
|
+
if (extraFieldLength > MAX_16_BITS) {
|
|
1334
|
+
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
1335
|
+
}
|
|
1164
1336
|
const {
|
|
1165
1337
|
headerArray,
|
|
1166
1338
|
headerView,
|
|
@@ -1176,29 +1348,27 @@ function getHeaderInfo(options) {
|
|
|
1176
1348
|
zip64UncompressedSize,
|
|
1177
1349
|
extraFieldLength
|
|
1178
1350
|
});
|
|
1179
|
-
|
|
1180
|
-
const localHeaderArray =
|
|
1351
|
+
const localHeader = createRecordWriter(HEADER_SIZE + getLength(rawFilename) + extraFieldLength);
|
|
1352
|
+
const localHeaderArray = localHeader.array;
|
|
1181
1353
|
const localHeaderView = getDataView(localHeaderArray);
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
localHeaderOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
1194
|
-
arraySet(localHeaderArray, rawExtraFieldNTFS, localHeaderOffset);
|
|
1195
|
-
localHeaderOffset += getLength(rawExtraFieldNTFS);
|
|
1196
|
-
arraySet(localHeaderArray, rawExtraFieldUnix, localHeaderOffset);
|
|
1197
|
-
localHeaderOffset += getLength(rawExtraFieldUnix);
|
|
1198
|
-
arraySet(localHeaderArray, rawExtraField, localHeaderOffset);
|
|
1354
|
+
localHeader.uint32(LOCAL_FILE_HEADER_SIGNATURE);
|
|
1355
|
+
localHeader.bytes(headerArray);
|
|
1356
|
+
localHeader.bytes(rawFilename);
|
|
1357
|
+
if (writeLocalExtraFieldZip64) {
|
|
1358
|
+
localHeader.bytes(rawLocalExtraFieldZip64);
|
|
1359
|
+
}
|
|
1360
|
+
localHeader.bytes(rawExtraFieldAES);
|
|
1361
|
+
localHeader.bytes(rawExtraFieldExtendedTimestamp);
|
|
1362
|
+
localHeader.bytes(rawExtraFieldNTFS);
|
|
1363
|
+
localHeader.bytes(rawExtraFieldUnix);
|
|
1364
|
+
localHeader.bytes(rawExtraField);
|
|
1199
1365
|
if (dataDescriptor) {
|
|
1200
|
-
|
|
1201
|
-
|
|
1366
|
+
if (!zip64CompressedSize) {
|
|
1367
|
+
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
1368
|
+
}
|
|
1369
|
+
if (!zip64UncompressedSize) {
|
|
1370
|
+
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
1371
|
+
}
|
|
1202
1372
|
}
|
|
1203
1373
|
return {
|
|
1204
1374
|
localHeaderArray,
|
|
@@ -1225,7 +1395,7 @@ function getHeaderInfo(options) {
|
|
|
1225
1395
|
function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
1226
1396
|
const { headerInfo } = entryInfo;
|
|
1227
1397
|
let { localHeaderArray, extraFieldLength } = headerInfo;
|
|
1228
|
-
let localHeaderArrayView
|
|
1398
|
+
let localHeaderArrayView;
|
|
1229
1399
|
let extraBytesLength = 64 - ((zipWriterOffset + getLength(localHeaderArray)) % 64);
|
|
1230
1400
|
if (extraBytesLength < 4) {
|
|
1231
1401
|
extraBytesLength += 64;
|
|
@@ -1233,13 +1403,14 @@ function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
|
1233
1403
|
const rawExtraFieldUSDZ = new Uint8Array(extraBytesLength);
|
|
1234
1404
|
const extraFieldUSDZView = getDataView(rawExtraFieldUSDZ);
|
|
1235
1405
|
setUint16(extraFieldUSDZView, 0, EXTRAFIELD_TYPE_USDZ);
|
|
1236
|
-
setUint16(extraFieldUSDZView, 2, extraBytesLength -
|
|
1406
|
+
setUint16(extraFieldUSDZView, 2, extraBytesLength - 4);
|
|
1237
1407
|
const previousLocalHeaderArray = localHeaderArray;
|
|
1238
1408
|
headerInfo.localHeaderArray = localHeaderArray = new Uint8Array(getLength(previousLocalHeaderArray) + extraBytesLength);
|
|
1239
1409
|
arraySet(localHeaderArray, previousLocalHeaderArray);
|
|
1240
1410
|
arraySet(localHeaderArray, rawExtraFieldUSDZ, getLength(previousLocalHeaderArray));
|
|
1241
1411
|
localHeaderArrayView = getDataView(localHeaderArray);
|
|
1242
1412
|
setUint16(localHeaderArrayView, 28, extraFieldLength + extraBytesLength);
|
|
1413
|
+
headerInfo.localHeaderView = localHeaderArrayView;
|
|
1243
1414
|
entryInfo.metadataSize += extraBytesLength;
|
|
1244
1415
|
}
|
|
1245
1416
|
|
|
@@ -1362,37 +1533,32 @@ function updateLocalHeader({
|
|
|
1362
1533
|
}, localHeaderView, { dataDescriptor }) {
|
|
1363
1534
|
if (!dataDescriptor) {
|
|
1364
1535
|
if (!encrypted) {
|
|
1365
|
-
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE +
|
|
1536
|
+
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, signature);
|
|
1366
1537
|
}
|
|
1367
1538
|
if (!zip64CompressedSize) {
|
|
1368
|
-
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE +
|
|
1539
|
+
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, compressedSize);
|
|
1369
1540
|
}
|
|
1370
1541
|
if (!zip64UncompressedSize) {
|
|
1371
|
-
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE +
|
|
1542
|
+
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, uncompressedSize);
|
|
1372
1543
|
}
|
|
1373
1544
|
}
|
|
1374
1545
|
if (zip64 && localExtraFieldZip64Length) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
}
|
|
1380
|
-
if (zip64CompressedSize) {
|
|
1381
|
-
setBigUint64(localHeaderView, localHeaderOffset, BigInt(compressedSize));
|
|
1382
|
-
localHeaderOffset += 8;
|
|
1383
|
-
}
|
|
1546
|
+
// the local zip64 extra field always contains both size slots
|
|
1547
|
+
const localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
|
|
1548
|
+
setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
|
|
1549
|
+
setBigUint64(localHeaderView, localHeaderOffset + 8, BigInt(compressedSize));
|
|
1384
1550
|
}
|
|
1385
1551
|
}
|
|
1386
1552
|
|
|
1387
1553
|
|
|
1388
1554
|
async function closeFile(zipWriter, comment, options) {
|
|
1389
|
-
const
|
|
1390
|
-
const {
|
|
1391
|
-
|
|
1392
|
-
|
|
1555
|
+
const directoryDataLength = createDirectoryRecords(zipWriter.files);
|
|
1556
|
+
const { cdStartDiskNumber, cdStartDiskOffset } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
|
|
1557
|
+
await writeEndOfDirectoryRecord(zipWriter, comment, options, { cdStartDiskNumber, cdStartDiskOffset, directoryDataLength });
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function createDirectoryRecords(files) {
|
|
1393
1561
|
let directoryDataLength = 0;
|
|
1394
|
-
let directoryOffset = zipWriter.offset - diskOffset;
|
|
1395
|
-
let filesLength = files.size;
|
|
1396
1562
|
for (const [, fileEntry] of files) {
|
|
1397
1563
|
const {
|
|
1398
1564
|
rawFilename,
|
|
@@ -1409,24 +1575,27 @@ async function closeFile(zipWriter, comment, options) {
|
|
|
1409
1575
|
uncompressedSize,
|
|
1410
1576
|
compressedSize
|
|
1411
1577
|
} = fileEntry;
|
|
1412
|
-
const zip64Offset = fileEntry.offset
|
|
1413
|
-
const zip64DiskNumberStart = fileEntry.diskNumberStart
|
|
1578
|
+
const zip64Offset = fileEntry.offset >= MAX_32_BITS;
|
|
1579
|
+
const zip64DiskNumberStart = fileEntry.diskNumberStart >= MAX_16_BITS;
|
|
1414
1580
|
let rawExtraFieldZip64;
|
|
1415
1581
|
if (zip64Offset || zip64DiskNumberStart || zip64UncompressedSize || zip64CompressedSize) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
if (
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
if (
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
if (zip64DiskNumberStart) {
|
|
1582
|
+
const length = 4 + (zip64UncompressedSize ? 8 : 0) + (zip64CompressedSize ? 8 : 0) + (zip64Offset ? 8 : 0) + (zip64DiskNumberStart ? 4 : 0);
|
|
1583
|
+
const extraFieldZip64 = createRecordWriter(length);
|
|
1584
|
+
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
1585
|
+
extraFieldZip64.uint16(length - 4);
|
|
1586
|
+
if (zip64UncompressedSize) {
|
|
1587
|
+
extraFieldZip64.uint64(uncompressedSize);
|
|
1588
|
+
}
|
|
1589
|
+
if (zip64CompressedSize) {
|
|
1590
|
+
extraFieldZip64.uint64(compressedSize);
|
|
1591
|
+
}
|
|
1592
|
+
if (zip64Offset) {
|
|
1593
|
+
extraFieldZip64.uint64(fileEntry.offset);
|
|
1594
|
+
}
|
|
1595
|
+
if (zip64DiskNumberStart) {
|
|
1596
|
+
extraFieldZip64.uint32(fileEntry.diskNumberStart);
|
|
1597
|
+
}
|
|
1598
|
+
rawExtraFieldZip64 = extraFieldZip64.array;
|
|
1430
1599
|
} else {
|
|
1431
1600
|
rawExtraFieldZip64 = new Uint8Array();
|
|
1432
1601
|
}
|
|
@@ -1434,32 +1603,41 @@ async function closeFile(zipWriter, comment, options) {
|
|
|
1434
1603
|
fileEntry.zip64Offset = zip64Offset;
|
|
1435
1604
|
fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
|
|
1436
1605
|
let rawExtraFieldTimestamp;
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
const
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1606
|
+
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
1607
|
+
if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
|
|
1608
|
+
const extraFieldTimestamp = createRecordWriter(9);
|
|
1609
|
+
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
1610
|
+
extraFieldTimestamp.uint16(5);
|
|
1611
|
+
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
1612
|
+
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
1613
|
+
rawExtraFieldTimestamp = extraFieldTimestamp.array;
|
|
1444
1614
|
} else {
|
|
1445
1615
|
rawExtraFieldTimestamp = new Uint8Array();
|
|
1446
1616
|
}
|
|
1447
1617
|
fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1618
|
+
const extraFieldLength = getLength(
|
|
1619
|
+
rawExtraFieldZip64,
|
|
1620
|
+
rawExtraFieldAES,
|
|
1621
|
+
rawExtraFieldNTFS,
|
|
1622
|
+
rawExtraFieldUnix,
|
|
1623
|
+
rawExtraFieldTimestamp,
|
|
1624
|
+
rawExtraField);
|
|
1625
|
+
if (extraFieldLength > MAX_16_BITS) {
|
|
1626
|
+
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
1627
|
+
}
|
|
1628
|
+
directoryDataLength += CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
1458
1629
|
}
|
|
1630
|
+
return directoryDataLength;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
|
|
1634
|
+
const { files, writer } = zipWriter;
|
|
1459
1635
|
const directoryArray = new Uint8Array(directoryDataLength);
|
|
1460
|
-
const directoryView = getDataView(directoryArray);
|
|
1461
1636
|
await initStream(writer);
|
|
1637
|
+
let offset = 0;
|
|
1462
1638
|
let directoryDiskOffset = 0;
|
|
1639
|
+
let cdStartDiskNumber = writer.diskNumber;
|
|
1640
|
+
let cdStartDiskOffset = writer.diskOffset;
|
|
1463
1641
|
for (const [indexFileEntry, fileEntry] of Array.from(files.values()).entries()) {
|
|
1464
1642
|
const {
|
|
1465
1643
|
offset: fileEntryOffset,
|
|
@@ -1485,52 +1663,46 @@ async function closeFile(zipWriter, comment, options) {
|
|
|
1485
1663
|
compressedSize
|
|
1486
1664
|
} = fileEntry;
|
|
1487
1665
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
1488
|
-
|
|
1489
|
-
|
|
1666
|
+
const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
1667
|
+
if (offset + directoryRecordLength - directoryDiskOffset > writer.availableSize) {
|
|
1668
|
+
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
1669
|
+
directoryDiskOffset = offset;
|
|
1670
|
+
writer.availableSize = 0;
|
|
1671
|
+
await writeData(writer, new Uint8Array());
|
|
1672
|
+
}
|
|
1673
|
+
if (indexFileEntry == 0) {
|
|
1674
|
+
cdStartDiskNumber = writer.diskNumber;
|
|
1675
|
+
cdStartDiskOffset = writer.diskOffset;
|
|
1676
|
+
}
|
|
1490
1677
|
if (!zip64UncompressedSize) {
|
|
1491
1678
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, uncompressedSize);
|
|
1492
1679
|
}
|
|
1493
1680
|
if (!zip64CompressedSize) {
|
|
1494
1681
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
|
|
1495
1682
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
setUint16(directoryView, directoryOffset, extraFieldLength);
|
|
1499
|
-
directoryOffset += 2;
|
|
1500
|
-
setUint16(directoryView, directoryOffset, getLength(rawComment));
|
|
1501
|
-
directoryOffset += 2;
|
|
1502
|
-
setUint16(directoryView, directoryOffset, zip64DiskNumberStart ? MAX_16_BITS : diskNumberStart);
|
|
1503
|
-
directoryOffset += 2;
|
|
1504
|
-
setUint16(directoryView, directoryOffset, internalFileAttributes);
|
|
1505
|
-
directoryOffset += 2;
|
|
1506
|
-
if (externalFileAttributes) {
|
|
1507
|
-
setUint32(directoryView, directoryOffset, externalFileAttributes);
|
|
1508
|
-
}
|
|
1509
|
-
directoryOffset += 4;
|
|
1510
|
-
setUint32(directoryView, directoryOffset, zip64Offset ? MAX_32_BITS : fileEntryOffset);
|
|
1511
|
-
directoryOffset += 4;
|
|
1512
|
-
arraySet(directoryArray, rawFilename, directoryOffset);
|
|
1513
|
-
directoryOffset += getLength(rawFilename);
|
|
1514
|
-
arraySet(directoryArray, rawExtraFieldZip64, directoryOffset);
|
|
1515
|
-
directoryOffset += getLength(rawExtraFieldZip64);
|
|
1516
|
-
arraySet(directoryArray, rawExtraFieldAES, directoryOffset);
|
|
1517
|
-
directoryOffset += getLength(rawExtraFieldAES);
|
|
1518
|
-
arraySet(directoryArray, rawExtraFieldExtendedTimestamp, directoryOffset);
|
|
1519
|
-
directoryOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
1520
|
-
arraySet(directoryArray, rawExtraFieldNTFS, directoryOffset);
|
|
1521
|
-
directoryOffset += getLength(rawExtraFieldNTFS);
|
|
1522
|
-
arraySet(directoryArray, rawExtraFieldUnix, directoryOffset);
|
|
1523
|
-
directoryOffset += getLength(rawExtraFieldUnix);
|
|
1524
|
-
arraySet(directoryArray, rawExtraField, directoryOffset);
|
|
1525
|
-
directoryOffset += getLength(rawExtraField);
|
|
1526
|
-
arraySet(directoryArray, rawComment, directoryOffset);
|
|
1527
|
-
directoryOffset += getLength(rawComment);
|
|
1528
|
-
if (offset - directoryDiskOffset > writer.availableSize) {
|
|
1529
|
-
writer.availableSize = 0;
|
|
1530
|
-
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
1531
|
-
directoryDiskOffset = offset;
|
|
1683
|
+
if ((zip64Offset || zip64DiskNumberStart) && fileEntry.version < VERSION_ZIP64) {
|
|
1684
|
+
setUint16(headerView, HEADER_OFFSET_VERSION, VERSION_ZIP64);
|
|
1532
1685
|
}
|
|
1533
|
-
|
|
1686
|
+
const directoryRecord = createRecordWriter(directoryRecordLength);
|
|
1687
|
+
directoryRecord.uint32(CENTRAL_FILE_HEADER_SIGNATURE);
|
|
1688
|
+
directoryRecord.uint16(versionMadeBy);
|
|
1689
|
+
directoryRecord.bytes(headerArray.subarray(0, HEADER_SIZE - 4 - 2));
|
|
1690
|
+
directoryRecord.uint16(extraFieldLength);
|
|
1691
|
+
directoryRecord.uint16(getLength(rawComment));
|
|
1692
|
+
directoryRecord.uint16(zip64DiskNumberStart ? MAX_16_BITS : diskNumberStart);
|
|
1693
|
+
directoryRecord.uint16(internalFileAttributes);
|
|
1694
|
+
directoryRecord.uint32(externalFileAttributes);
|
|
1695
|
+
directoryRecord.uint32(zip64Offset ? MAX_32_BITS : fileEntryOffset);
|
|
1696
|
+
directoryRecord.bytes(rawFilename);
|
|
1697
|
+
directoryRecord.bytes(rawExtraFieldZip64);
|
|
1698
|
+
directoryRecord.bytes(rawExtraFieldAES);
|
|
1699
|
+
directoryRecord.bytes(rawExtraFieldExtendedTimestamp);
|
|
1700
|
+
directoryRecord.bytes(rawExtraFieldNTFS);
|
|
1701
|
+
directoryRecord.bytes(rawExtraFieldUnix);
|
|
1702
|
+
directoryRecord.bytes(rawExtraField);
|
|
1703
|
+
directoryRecord.bytes(rawComment);
|
|
1704
|
+
arraySet(directoryArray, directoryRecord.array, offset);
|
|
1705
|
+
offset += directoryRecordLength;
|
|
1534
1706
|
if (options.onprogress) {
|
|
1535
1707
|
try {
|
|
1536
1708
|
await options.onprogress(indexFileEntry + 1, files.size, new Entry(fileEntry));
|
|
@@ -1540,36 +1712,53 @@ async function closeFile(zipWriter, comment, options) {
|
|
|
1540
1712
|
}
|
|
1541
1713
|
}
|
|
1542
1714
|
await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
|
|
1715
|
+
return { cdStartDiskNumber, cdStartDiskOffset };
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
|
|
1719
|
+
const { writer } = zipWriter;
|
|
1720
|
+
const { cdStartDiskNumber, cdStartDiskOffset } = cdInfo;
|
|
1721
|
+
let { directoryDataLength } = cdInfo;
|
|
1722
|
+
let filesLength = zipWriter.files.size;
|
|
1723
|
+
let diskNumber = cdStartDiskNumber;
|
|
1724
|
+
let directoryOffset = zipWriter.offset - cdStartDiskOffset - (cdStartDiskNumber ? zipWriter.initialOffset : 0);
|
|
1543
1725
|
let lastDiskNumber = writer.diskNumber;
|
|
1544
|
-
|
|
1545
|
-
if (availableSize < END_OF_CENTRAL_DIR_LENGTH) {
|
|
1726
|
+
if (writer.availableSize < END_OF_CENTRAL_DIR_LENGTH) {
|
|
1546
1727
|
lastDiskNumber++;
|
|
1547
1728
|
}
|
|
1548
1729
|
let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
1549
|
-
if (directoryOffset
|
|
1730
|
+
if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || filesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
|
|
1550
1731
|
if (zip64 === false) {
|
|
1551
1732
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
1552
1733
|
} else {
|
|
1553
1734
|
zip64 = true;
|
|
1554
1735
|
}
|
|
1555
1736
|
}
|
|
1556
|
-
const
|
|
1557
|
-
|
|
1558
|
-
|
|
1737
|
+
const commentLength = getLength(comment);
|
|
1738
|
+
if (commentLength > MAX_16_BITS) {
|
|
1739
|
+
throw new Error(ERR_INVALID_COMMENT);
|
|
1740
|
+
}
|
|
1741
|
+
const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
|
|
1742
|
+
if (getLength(endOfdirectoryRecord.array) + commentLength > writer.availableSize) {
|
|
1743
|
+
writer.availableSize = 0;
|
|
1744
|
+
await writeData(writer, new Uint8Array());
|
|
1745
|
+
}
|
|
1746
|
+
lastDiskNumber = writer.diskNumber;
|
|
1559
1747
|
if (zip64) {
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1748
|
+
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
|
|
1749
|
+
endOfdirectoryRecord.uint64(44);
|
|
1750
|
+
endOfdirectoryRecord.uint16(45);
|
|
1751
|
+
endOfdirectoryRecord.uint16(45);
|
|
1752
|
+
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
1753
|
+
endOfdirectoryRecord.uint32(diskNumber);
|
|
1754
|
+
endOfdirectoryRecord.uint64(filesLength);
|
|
1755
|
+
endOfdirectoryRecord.uint64(filesLength);
|
|
1756
|
+
endOfdirectoryRecord.uint64(directoryDataLength);
|
|
1757
|
+
endOfdirectoryRecord.uint64(directoryOffset);
|
|
1758
|
+
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
|
|
1759
|
+
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
1760
|
+
endOfdirectoryRecord.uint64(BigInt(zipWriter.offset) + BigInt(directoryDataLength) - BigInt(writer.diskOffset) - BigInt(writer.diskNumber ? zipWriter.initialOffset : 0));
|
|
1761
|
+
endOfdirectoryRecord.uint32(lastDiskNumber + 1);
|
|
1573
1762
|
const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
|
|
1574
1763
|
if (supportZip64SplitFile) {
|
|
1575
1764
|
lastDiskNumber = MAX_16_BITS;
|
|
@@ -1578,29 +1767,40 @@ async function closeFile(zipWriter, comment, options) {
|
|
|
1578
1767
|
filesLength = MAX_16_BITS;
|
|
1579
1768
|
directoryOffset = MAX_32_BITS;
|
|
1580
1769
|
directoryDataLength = MAX_32_BITS;
|
|
1581
|
-
offset += ZIP64_END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
|
|
1582
|
-
}
|
|
1583
|
-
setUint32(endOfdirectoryView, offset, END_OF_CENTRAL_DIR_SIGNATURE);
|
|
1584
|
-
setUint16(endOfdirectoryView, offset + 4, lastDiskNumber);
|
|
1585
|
-
setUint16(endOfdirectoryView, offset + 6, diskNumber);
|
|
1586
|
-
setUint16(endOfdirectoryView, offset + 8, filesLength);
|
|
1587
|
-
setUint16(endOfdirectoryView, offset + 10, filesLength);
|
|
1588
|
-
setUint32(endOfdirectoryView, offset + 12, directoryDataLength);
|
|
1589
|
-
setUint32(endOfdirectoryView, offset + 16, directoryOffset);
|
|
1590
|
-
const commentLength = getLength(comment);
|
|
1591
|
-
if (commentLength) {
|
|
1592
|
-
if (commentLength <= MAX_16_BITS) {
|
|
1593
|
-
setUint16(endOfdirectoryView, offset + 20, commentLength);
|
|
1594
|
-
} else {
|
|
1595
|
-
throw new Error(ERR_INVALID_COMMENT);
|
|
1596
|
-
}
|
|
1597
1770
|
}
|
|
1598
|
-
|
|
1771
|
+
endOfdirectoryRecord.uint32(END_OF_CENTRAL_DIR_SIGNATURE);
|
|
1772
|
+
endOfdirectoryRecord.uint16(lastDiskNumber);
|
|
1773
|
+
endOfdirectoryRecord.uint16(diskNumber);
|
|
1774
|
+
endOfdirectoryRecord.uint16(filesLength);
|
|
1775
|
+
endOfdirectoryRecord.uint16(filesLength);
|
|
1776
|
+
endOfdirectoryRecord.uint32(directoryDataLength);
|
|
1777
|
+
endOfdirectoryRecord.uint32(directoryOffset);
|
|
1778
|
+
endOfdirectoryRecord.uint16(commentLength);
|
|
1779
|
+
await writeData(writer, endOfdirectoryRecord.array);
|
|
1599
1780
|
if (commentLength) {
|
|
1600
1781
|
await writeData(writer, comment);
|
|
1601
1782
|
}
|
|
1602
1783
|
}
|
|
1603
1784
|
|
|
1785
|
+
function createRecordWriter(length) {
|
|
1786
|
+
const array = new Uint8Array(length);
|
|
1787
|
+
const view = getDataView(array);
|
|
1788
|
+
let offset = 0;
|
|
1789
|
+
return {
|
|
1790
|
+
array,
|
|
1791
|
+
uint8: value => { setUint8(view, offset, value); offset += 1; },
|
|
1792
|
+
uint16: value => { setUint16(view, offset, value); offset += 2; },
|
|
1793
|
+
uint32: value => { setUint32(view, offset, value); offset += 4; },
|
|
1794
|
+
uint64: value => { setBigUint64(view, offset, BigInt(value)); offset += 8; },
|
|
1795
|
+
bytes: value => { arraySet(array, value, offset); offset += getLength(value); },
|
|
1796
|
+
skip: count => offset += count
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
function getSegmentOffset(zipWriter, writer) {
|
|
1801
|
+
return zipWriter.offset - writer.diskOffset - (writer.diskNumber ? zipWriter.initialOffset : 0);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1604
1804
|
async function writeData(writer, array) {
|
|
1605
1805
|
const { writable } = writer;
|
|
1606
1806
|
const streamWriter = writable.getWriter();
|
|
@@ -1613,12 +1813,40 @@ async function writeData(writer, array) {
|
|
|
1613
1813
|
}
|
|
1614
1814
|
}
|
|
1615
1815
|
|
|
1816
|
+
async function flushBufferedData(readable, writer, signal, onChunkWritten) {
|
|
1817
|
+
const streamWriter = writer.writable.getWriter();
|
|
1818
|
+
try {
|
|
1819
|
+
await readable.pipeTo(new WritableStream({
|
|
1820
|
+
async write(chunk) {
|
|
1821
|
+
await streamWriter.ready;
|
|
1822
|
+
await streamWriter.write(chunk);
|
|
1823
|
+
onChunkWritten(getLength(chunk));
|
|
1824
|
+
}
|
|
1825
|
+
}), { preventClose: true, preventAbort: true, signal });
|
|
1826
|
+
} finally {
|
|
1827
|
+
streamWriter.releaseLock();
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1616
1831
|
function getTimeNTFS(date) {
|
|
1617
1832
|
if (date) {
|
|
1618
1833
|
return ((BigInt(date.getTime()) + BigInt(11644473600000)) * BigInt(10000));
|
|
1619
1834
|
}
|
|
1620
1835
|
}
|
|
1621
1836
|
|
|
1837
|
+
function getTimeUnix(date) {
|
|
1838
|
+
return Math.floor(date.getTime() / 1000);
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// the extended timestamp extra field stores dates as signed 32-bit time_t values
|
|
1842
|
+
function inUnixTimeRange(timeUnix) {
|
|
1843
|
+
return timeUnix >= MIN_UNIX_TIME && timeUnix <= MAX_UNIX_TIME;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function clampUnixTime(timeUnix) {
|
|
1847
|
+
return Math.min(MAX_UNIX_TIME, Math.max(MIN_UNIX_TIME, timeUnix));
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1622
1850
|
function getOptionValue(zipWriter, options, name, defaultValue) {
|
|
1623
1851
|
const result = options[name] === UNDEFINED_VALUE ? zipWriter.options[name] : options[name];
|
|
1624
1852
|
return result === UNDEFINED_VALUE ? defaultValue : result;
|
|
@@ -1649,7 +1877,7 @@ function arraySet(array, typedArray, offset) {
|
|
|
1649
1877
|
}
|
|
1650
1878
|
|
|
1651
1879
|
function getDataView(array) {
|
|
1652
|
-
return new DataView(array.buffer);
|
|
1880
|
+
return new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
1653
1881
|
}
|
|
1654
1882
|
|
|
1655
1883
|
function getLength(...arrayLikes) {
|
|
@@ -1665,30 +1893,39 @@ function getHeaderArrayData({
|
|
|
1665
1893
|
uncompressedSize,
|
|
1666
1894
|
compressedSize,
|
|
1667
1895
|
lastModDate,
|
|
1896
|
+
rawLastModDate,
|
|
1668
1897
|
rawFilename,
|
|
1669
1898
|
zip64CompressedSize,
|
|
1670
1899
|
zip64UncompressedSize,
|
|
1671
1900
|
extraFieldLength
|
|
1672
1901
|
}) {
|
|
1673
|
-
const
|
|
1902
|
+
const headerRecord = createRecordWriter(HEADER_SIZE - 4);
|
|
1903
|
+
const headerArray = headerRecord.array;
|
|
1674
1904
|
const headerView = getDataView(headerArray);
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1905
|
+
headerRecord.uint16(version);
|
|
1906
|
+
headerRecord.uint16(bitFlag);
|
|
1907
|
+
headerRecord.uint16(compressionMethod);
|
|
1908
|
+
if (rawLastModDate === UNDEFINED_VALUE) {
|
|
1909
|
+
const dateArray = new Uint32Array(1);
|
|
1910
|
+
const dateView = getDataView(dateArray);
|
|
1911
|
+
setUint16(dateView, 0, (((lastModDate.getHours() << 6) | lastModDate.getMinutes()) << 5) | lastModDate.getSeconds() / 2);
|
|
1912
|
+
setUint16(dateView, 2, ((((lastModDate.getFullYear() - 1980) << 4) | (lastModDate.getMonth() + 1)) << 5) | lastModDate.getDate());
|
|
1913
|
+
rawLastModDate = dateArray[0];
|
|
1914
|
+
}
|
|
1915
|
+
headerRecord.uint32(rawLastModDate);
|
|
1916
|
+
headerRecord.skip(4);
|
|
1684
1917
|
if (zip64CompressedSize || compressedSize !== UNDEFINED_VALUE) {
|
|
1685
|
-
|
|
1918
|
+
headerRecord.uint32(zip64CompressedSize ? MAX_32_BITS : compressedSize);
|
|
1919
|
+
} else {
|
|
1920
|
+
headerRecord.skip(4);
|
|
1686
1921
|
}
|
|
1687
1922
|
if (zip64UncompressedSize || uncompressedSize !== UNDEFINED_VALUE) {
|
|
1688
|
-
|
|
1923
|
+
headerRecord.uint32(zip64UncompressedSize ? MAX_32_BITS : uncompressedSize);
|
|
1924
|
+
} else {
|
|
1925
|
+
headerRecord.skip(4);
|
|
1689
1926
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1927
|
+
headerRecord.uint16(getLength(rawFilename));
|
|
1928
|
+
headerRecord.uint16(extraFieldLength);
|
|
1692
1929
|
return {
|
|
1693
1930
|
headerArray,
|
|
1694
1931
|
headerView,
|