11cb0ef41Sopenharmony_ci'use strict' 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci// this[BUFFER] is the remainder of a chunk if we're waiting for 41cb0ef41Sopenharmony_ci// the full 512 bytes of a header to come in. We will Buffer.concat() 51cb0ef41Sopenharmony_ci// it to the next write(), which is a mem copy, but a small one. 61cb0ef41Sopenharmony_ci// 71cb0ef41Sopenharmony_ci// this[QUEUE] is a Yallist of entries that haven't been emitted 81cb0ef41Sopenharmony_ci// yet this can only get filled up if the user keeps write()ing after 91cb0ef41Sopenharmony_ci// a write() returns false, or does a write() with more than one entry 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// We don't buffer chunks, we always parse them and either create an 121cb0ef41Sopenharmony_ci// entry, or push it into the active entry. The ReadEntry class knows 131cb0ef41Sopenharmony_ci// to throw data away if .ignore=true 141cb0ef41Sopenharmony_ci// 151cb0ef41Sopenharmony_ci// Shift entry off the buffer when it emits 'end', and emit 'entry' for 161cb0ef41Sopenharmony_ci// the next one in the list. 171cb0ef41Sopenharmony_ci// 181cb0ef41Sopenharmony_ci// At any time, we're pushing body chunks into the entry at WRITEENTRY, 191cb0ef41Sopenharmony_ci// and waiting for 'end' on the entry at READENTRY 201cb0ef41Sopenharmony_ci// 211cb0ef41Sopenharmony_ci// ignored entries get .resume() called on them straight away 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_ciconst warner = require('./warn-mixin.js') 241cb0ef41Sopenharmony_ciconst Header = require('./header.js') 251cb0ef41Sopenharmony_ciconst EE = require('events') 261cb0ef41Sopenharmony_ciconst Yallist = require('yallist') 271cb0ef41Sopenharmony_ciconst maxMetaEntrySize = 1024 * 1024 281cb0ef41Sopenharmony_ciconst Entry = require('./read-entry.js') 291cb0ef41Sopenharmony_ciconst Pax = require('./pax.js') 301cb0ef41Sopenharmony_ciconst zlib = require('minizlib') 311cb0ef41Sopenharmony_ciconst { nextTick } = require('process') 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ciconst gzipHeader = Buffer.from([0x1f, 0x8b]) 341cb0ef41Sopenharmony_ciconst STATE = Symbol('state') 351cb0ef41Sopenharmony_ciconst WRITEENTRY = Symbol('writeEntry') 361cb0ef41Sopenharmony_ciconst READENTRY = Symbol('readEntry') 371cb0ef41Sopenharmony_ciconst NEXTENTRY = Symbol('nextEntry') 381cb0ef41Sopenharmony_ciconst PROCESSENTRY = Symbol('processEntry') 391cb0ef41Sopenharmony_ciconst EX = Symbol('extendedHeader') 401cb0ef41Sopenharmony_ciconst GEX = Symbol('globalExtendedHeader') 411cb0ef41Sopenharmony_ciconst META = Symbol('meta') 421cb0ef41Sopenharmony_ciconst EMITMETA = Symbol('emitMeta') 431cb0ef41Sopenharmony_ciconst BUFFER = Symbol('buffer') 441cb0ef41Sopenharmony_ciconst QUEUE = Symbol('queue') 451cb0ef41Sopenharmony_ciconst ENDED = Symbol('ended') 461cb0ef41Sopenharmony_ciconst EMITTEDEND = Symbol('emittedEnd') 471cb0ef41Sopenharmony_ciconst EMIT = Symbol('emit') 481cb0ef41Sopenharmony_ciconst UNZIP = Symbol('unzip') 491cb0ef41Sopenharmony_ciconst CONSUMECHUNK = Symbol('consumeChunk') 501cb0ef41Sopenharmony_ciconst CONSUMECHUNKSUB = Symbol('consumeChunkSub') 511cb0ef41Sopenharmony_ciconst CONSUMEBODY = Symbol('consumeBody') 521cb0ef41Sopenharmony_ciconst CONSUMEMETA = Symbol('consumeMeta') 531cb0ef41Sopenharmony_ciconst CONSUMEHEADER = Symbol('consumeHeader') 541cb0ef41Sopenharmony_ciconst CONSUMING = Symbol('consuming') 551cb0ef41Sopenharmony_ciconst BUFFERCONCAT = Symbol('bufferConcat') 561cb0ef41Sopenharmony_ciconst MAYBEEND = Symbol('maybeEnd') 571cb0ef41Sopenharmony_ciconst WRITING = Symbol('writing') 581cb0ef41Sopenharmony_ciconst ABORTED = Symbol('aborted') 591cb0ef41Sopenharmony_ciconst DONE = Symbol('onDone') 601cb0ef41Sopenharmony_ciconst SAW_VALID_ENTRY = Symbol('sawValidEntry') 611cb0ef41Sopenharmony_ciconst SAW_NULL_BLOCK = Symbol('sawNullBlock') 621cb0ef41Sopenharmony_ciconst SAW_EOF = Symbol('sawEOF') 631cb0ef41Sopenharmony_ciconst CLOSESTREAM = Symbol('closeStream') 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ciconst noop = _ => true 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_cimodule.exports = warner(class Parser extends EE { 681cb0ef41Sopenharmony_ci constructor (opt) { 691cb0ef41Sopenharmony_ci opt = opt || {} 701cb0ef41Sopenharmony_ci super(opt) 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci this.file = opt.file || '' 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci // set to boolean false when an entry starts. 1024 bytes of \0 751cb0ef41Sopenharmony_ci // is technically a valid tarball, albeit a boring one. 761cb0ef41Sopenharmony_ci this[SAW_VALID_ENTRY] = null 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci // these BADARCHIVE errors can't be detected early. listen on DONE. 791cb0ef41Sopenharmony_ci this.on(DONE, _ => { 801cb0ef41Sopenharmony_ci if (this[STATE] === 'begin' || this[SAW_VALID_ENTRY] === false) { 811cb0ef41Sopenharmony_ci // either less than 1 block of data, or all entries were invalid. 821cb0ef41Sopenharmony_ci // Either way, probably not even a tarball. 831cb0ef41Sopenharmony_ci this.warn('TAR_BAD_ARCHIVE', 'Unrecognized archive format') 841cb0ef41Sopenharmony_ci } 851cb0ef41Sopenharmony_ci }) 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci if (opt.ondone) { 881cb0ef41Sopenharmony_ci this.on(DONE, opt.ondone) 891cb0ef41Sopenharmony_ci } else { 901cb0ef41Sopenharmony_ci this.on(DONE, _ => { 911cb0ef41Sopenharmony_ci this.emit('prefinish') 921cb0ef41Sopenharmony_ci this.emit('finish') 931cb0ef41Sopenharmony_ci this.emit('end') 941cb0ef41Sopenharmony_ci }) 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci this.strict = !!opt.strict 981cb0ef41Sopenharmony_ci this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize 991cb0ef41Sopenharmony_ci this.filter = typeof opt.filter === 'function' ? opt.filter : noop 1001cb0ef41Sopenharmony_ci // Unlike gzip, brotli doesn't have any magic bytes to identify it 1011cb0ef41Sopenharmony_ci // Users need to explicitly tell us they're extracting a brotli file 1021cb0ef41Sopenharmony_ci // Or we infer from the file extension 1031cb0ef41Sopenharmony_ci const isTBR = (opt.file && ( 1041cb0ef41Sopenharmony_ci opt.file.endsWith('.tar.br') || opt.file.endsWith('.tbr'))) 1051cb0ef41Sopenharmony_ci // if it's a tbr file it MIGHT be brotli, but we don't know until 1061cb0ef41Sopenharmony_ci // we look at it and verify it's not a valid tar file. 1071cb0ef41Sopenharmony_ci this.brotli = !opt.gzip && opt.brotli !== undefined ? opt.brotli 1081cb0ef41Sopenharmony_ci : isTBR ? undefined 1091cb0ef41Sopenharmony_ci : false 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci // have to set this so that streams are ok piping into it 1121cb0ef41Sopenharmony_ci this.writable = true 1131cb0ef41Sopenharmony_ci this.readable = false 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci this[QUEUE] = new Yallist() 1161cb0ef41Sopenharmony_ci this[BUFFER] = null 1171cb0ef41Sopenharmony_ci this[READENTRY] = null 1181cb0ef41Sopenharmony_ci this[WRITEENTRY] = null 1191cb0ef41Sopenharmony_ci this[STATE] = 'begin' 1201cb0ef41Sopenharmony_ci this[META] = '' 1211cb0ef41Sopenharmony_ci this[EX] = null 1221cb0ef41Sopenharmony_ci this[GEX] = null 1231cb0ef41Sopenharmony_ci this[ENDED] = false 1241cb0ef41Sopenharmony_ci this[UNZIP] = null 1251cb0ef41Sopenharmony_ci this[ABORTED] = false 1261cb0ef41Sopenharmony_ci this[SAW_NULL_BLOCK] = false 1271cb0ef41Sopenharmony_ci this[SAW_EOF] = false 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci this.on('end', () => this[CLOSESTREAM]()) 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci if (typeof opt.onwarn === 'function') { 1321cb0ef41Sopenharmony_ci this.on('warn', opt.onwarn) 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci if (typeof opt.onentry === 'function') { 1351cb0ef41Sopenharmony_ci this.on('entry', opt.onentry) 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci } 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci [CONSUMEHEADER] (chunk, position) { 1401cb0ef41Sopenharmony_ci if (this[SAW_VALID_ENTRY] === null) { 1411cb0ef41Sopenharmony_ci this[SAW_VALID_ENTRY] = false 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci let header 1441cb0ef41Sopenharmony_ci try { 1451cb0ef41Sopenharmony_ci header = new Header(chunk, position, this[EX], this[GEX]) 1461cb0ef41Sopenharmony_ci } catch (er) { 1471cb0ef41Sopenharmony_ci return this.warn('TAR_ENTRY_INVALID', er) 1481cb0ef41Sopenharmony_ci } 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci if (header.nullBlock) { 1511cb0ef41Sopenharmony_ci if (this[SAW_NULL_BLOCK]) { 1521cb0ef41Sopenharmony_ci this[SAW_EOF] = true 1531cb0ef41Sopenharmony_ci // ending an archive with no entries. pointless, but legal. 1541cb0ef41Sopenharmony_ci if (this[STATE] === 'begin') { 1551cb0ef41Sopenharmony_ci this[STATE] = 'header' 1561cb0ef41Sopenharmony_ci } 1571cb0ef41Sopenharmony_ci this[EMIT]('eof') 1581cb0ef41Sopenharmony_ci } else { 1591cb0ef41Sopenharmony_ci this[SAW_NULL_BLOCK] = true 1601cb0ef41Sopenharmony_ci this[EMIT]('nullBlock') 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci } else { 1631cb0ef41Sopenharmony_ci this[SAW_NULL_BLOCK] = false 1641cb0ef41Sopenharmony_ci if (!header.cksumValid) { 1651cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_INVALID', 'checksum failure', { header }) 1661cb0ef41Sopenharmony_ci } else if (!header.path) { 1671cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_INVALID', 'path is required', { header }) 1681cb0ef41Sopenharmony_ci } else { 1691cb0ef41Sopenharmony_ci const type = header.type 1701cb0ef41Sopenharmony_ci if (/^(Symbolic)?Link$/.test(type) && !header.linkpath) { 1711cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_INVALID', 'linkpath required', { header }) 1721cb0ef41Sopenharmony_ci } else if (!/^(Symbolic)?Link$/.test(type) && header.linkpath) { 1731cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_INVALID', 'linkpath forbidden', { header }) 1741cb0ef41Sopenharmony_ci } else { 1751cb0ef41Sopenharmony_ci const entry = this[WRITEENTRY] = new Entry(header, this[EX], this[GEX]) 1761cb0ef41Sopenharmony_ci 1771cb0ef41Sopenharmony_ci // we do this for meta & ignored entries as well, because they 1781cb0ef41Sopenharmony_ci // are still valid tar, or else we wouldn't know to ignore them 1791cb0ef41Sopenharmony_ci if (!this[SAW_VALID_ENTRY]) { 1801cb0ef41Sopenharmony_ci if (entry.remain) { 1811cb0ef41Sopenharmony_ci // this might be the one! 1821cb0ef41Sopenharmony_ci const onend = () => { 1831cb0ef41Sopenharmony_ci if (!entry.invalid) { 1841cb0ef41Sopenharmony_ci this[SAW_VALID_ENTRY] = true 1851cb0ef41Sopenharmony_ci } 1861cb0ef41Sopenharmony_ci } 1871cb0ef41Sopenharmony_ci entry.on('end', onend) 1881cb0ef41Sopenharmony_ci } else { 1891cb0ef41Sopenharmony_ci this[SAW_VALID_ENTRY] = true 1901cb0ef41Sopenharmony_ci } 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci if (entry.meta) { 1941cb0ef41Sopenharmony_ci if (entry.size > this.maxMetaEntrySize) { 1951cb0ef41Sopenharmony_ci entry.ignore = true 1961cb0ef41Sopenharmony_ci this[EMIT]('ignoredEntry', entry) 1971cb0ef41Sopenharmony_ci this[STATE] = 'ignore' 1981cb0ef41Sopenharmony_ci entry.resume() 1991cb0ef41Sopenharmony_ci } else if (entry.size > 0) { 2001cb0ef41Sopenharmony_ci this[META] = '' 2011cb0ef41Sopenharmony_ci entry.on('data', c => this[META] += c) 2021cb0ef41Sopenharmony_ci this[STATE] = 'meta' 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci } else { 2051cb0ef41Sopenharmony_ci this[EX] = null 2061cb0ef41Sopenharmony_ci entry.ignore = entry.ignore || !this.filter(entry.path, entry) 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci if (entry.ignore) { 2091cb0ef41Sopenharmony_ci // probably valid, just not something we care about 2101cb0ef41Sopenharmony_ci this[EMIT]('ignoredEntry', entry) 2111cb0ef41Sopenharmony_ci this[STATE] = entry.remain ? 'ignore' : 'header' 2121cb0ef41Sopenharmony_ci entry.resume() 2131cb0ef41Sopenharmony_ci } else { 2141cb0ef41Sopenharmony_ci if (entry.remain) { 2151cb0ef41Sopenharmony_ci this[STATE] = 'body' 2161cb0ef41Sopenharmony_ci } else { 2171cb0ef41Sopenharmony_ci this[STATE] = 'header' 2181cb0ef41Sopenharmony_ci entry.end() 2191cb0ef41Sopenharmony_ci } 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci if (!this[READENTRY]) { 2221cb0ef41Sopenharmony_ci this[QUEUE].push(entry) 2231cb0ef41Sopenharmony_ci this[NEXTENTRY]() 2241cb0ef41Sopenharmony_ci } else { 2251cb0ef41Sopenharmony_ci this[QUEUE].push(entry) 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci } 2281cb0ef41Sopenharmony_ci } 2291cb0ef41Sopenharmony_ci } 2301cb0ef41Sopenharmony_ci } 2311cb0ef41Sopenharmony_ci } 2321cb0ef41Sopenharmony_ci } 2331cb0ef41Sopenharmony_ci 2341cb0ef41Sopenharmony_ci [CLOSESTREAM] () { 2351cb0ef41Sopenharmony_ci nextTick(() => this.emit('close')) 2361cb0ef41Sopenharmony_ci } 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_ci [PROCESSENTRY] (entry) { 2391cb0ef41Sopenharmony_ci let go = true 2401cb0ef41Sopenharmony_ci 2411cb0ef41Sopenharmony_ci if (!entry) { 2421cb0ef41Sopenharmony_ci this[READENTRY] = null 2431cb0ef41Sopenharmony_ci go = false 2441cb0ef41Sopenharmony_ci } else if (Array.isArray(entry)) { 2451cb0ef41Sopenharmony_ci this.emit.apply(this, entry) 2461cb0ef41Sopenharmony_ci } else { 2471cb0ef41Sopenharmony_ci this[READENTRY] = entry 2481cb0ef41Sopenharmony_ci this.emit('entry', entry) 2491cb0ef41Sopenharmony_ci if (!entry.emittedEnd) { 2501cb0ef41Sopenharmony_ci entry.on('end', _ => this[NEXTENTRY]()) 2511cb0ef41Sopenharmony_ci go = false 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci } 2541cb0ef41Sopenharmony_ci 2551cb0ef41Sopenharmony_ci return go 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci 2581cb0ef41Sopenharmony_ci [NEXTENTRY] () { 2591cb0ef41Sopenharmony_ci do {} while (this[PROCESSENTRY](this[QUEUE].shift())) 2601cb0ef41Sopenharmony_ci 2611cb0ef41Sopenharmony_ci if (!this[QUEUE].length) { 2621cb0ef41Sopenharmony_ci // At this point, there's nothing in the queue, but we may have an 2631cb0ef41Sopenharmony_ci // entry which is being consumed (readEntry). 2641cb0ef41Sopenharmony_ci // If we don't, then we definitely can handle more data. 2651cb0ef41Sopenharmony_ci // If we do, and either it's flowing, or it has never had any data 2661cb0ef41Sopenharmony_ci // written to it, then it needs more. 2671cb0ef41Sopenharmony_ci // The only other possibility is that it has returned false from a 2681cb0ef41Sopenharmony_ci // write() call, so we wait for the next drain to continue. 2691cb0ef41Sopenharmony_ci const re = this[READENTRY] 2701cb0ef41Sopenharmony_ci const drainNow = !re || re.flowing || re.size === re.remain 2711cb0ef41Sopenharmony_ci if (drainNow) { 2721cb0ef41Sopenharmony_ci if (!this[WRITING]) { 2731cb0ef41Sopenharmony_ci this.emit('drain') 2741cb0ef41Sopenharmony_ci } 2751cb0ef41Sopenharmony_ci } else { 2761cb0ef41Sopenharmony_ci re.once('drain', _ => this.emit('drain')) 2771cb0ef41Sopenharmony_ci } 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci } 2801cb0ef41Sopenharmony_ci 2811cb0ef41Sopenharmony_ci [CONSUMEBODY] (chunk, position) { 2821cb0ef41Sopenharmony_ci // write up to but no more than writeEntry.blockRemain 2831cb0ef41Sopenharmony_ci const entry = this[WRITEENTRY] 2841cb0ef41Sopenharmony_ci const br = entry.blockRemain 2851cb0ef41Sopenharmony_ci const c = (br >= chunk.length && position === 0) ? chunk 2861cb0ef41Sopenharmony_ci : chunk.slice(position, position + br) 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci entry.write(c) 2891cb0ef41Sopenharmony_ci 2901cb0ef41Sopenharmony_ci if (!entry.blockRemain) { 2911cb0ef41Sopenharmony_ci this[STATE] = 'header' 2921cb0ef41Sopenharmony_ci this[WRITEENTRY] = null 2931cb0ef41Sopenharmony_ci entry.end() 2941cb0ef41Sopenharmony_ci } 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci return c.length 2971cb0ef41Sopenharmony_ci } 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci [CONSUMEMETA] (chunk, position) { 3001cb0ef41Sopenharmony_ci const entry = this[WRITEENTRY] 3011cb0ef41Sopenharmony_ci const ret = this[CONSUMEBODY](chunk, position) 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci // if we finished, then the entry is reset 3041cb0ef41Sopenharmony_ci if (!this[WRITEENTRY]) { 3051cb0ef41Sopenharmony_ci this[EMITMETA](entry) 3061cb0ef41Sopenharmony_ci } 3071cb0ef41Sopenharmony_ci 3081cb0ef41Sopenharmony_ci return ret 3091cb0ef41Sopenharmony_ci } 3101cb0ef41Sopenharmony_ci 3111cb0ef41Sopenharmony_ci [EMIT] (ev, data, extra) { 3121cb0ef41Sopenharmony_ci if (!this[QUEUE].length && !this[READENTRY]) { 3131cb0ef41Sopenharmony_ci this.emit(ev, data, extra) 3141cb0ef41Sopenharmony_ci } else { 3151cb0ef41Sopenharmony_ci this[QUEUE].push([ev, data, extra]) 3161cb0ef41Sopenharmony_ci } 3171cb0ef41Sopenharmony_ci } 3181cb0ef41Sopenharmony_ci 3191cb0ef41Sopenharmony_ci [EMITMETA] (entry) { 3201cb0ef41Sopenharmony_ci this[EMIT]('meta', this[META]) 3211cb0ef41Sopenharmony_ci switch (entry.type) { 3221cb0ef41Sopenharmony_ci case 'ExtendedHeader': 3231cb0ef41Sopenharmony_ci case 'OldExtendedHeader': 3241cb0ef41Sopenharmony_ci this[EX] = Pax.parse(this[META], this[EX], false) 3251cb0ef41Sopenharmony_ci break 3261cb0ef41Sopenharmony_ci 3271cb0ef41Sopenharmony_ci case 'GlobalExtendedHeader': 3281cb0ef41Sopenharmony_ci this[GEX] = Pax.parse(this[META], this[GEX], true) 3291cb0ef41Sopenharmony_ci break 3301cb0ef41Sopenharmony_ci 3311cb0ef41Sopenharmony_ci case 'NextFileHasLongPath': 3321cb0ef41Sopenharmony_ci case 'OldGnuLongPath': 3331cb0ef41Sopenharmony_ci this[EX] = this[EX] || Object.create(null) 3341cb0ef41Sopenharmony_ci this[EX].path = this[META].replace(/\0.*/, '') 3351cb0ef41Sopenharmony_ci break 3361cb0ef41Sopenharmony_ci 3371cb0ef41Sopenharmony_ci case 'NextFileHasLongLinkpath': 3381cb0ef41Sopenharmony_ci this[EX] = this[EX] || Object.create(null) 3391cb0ef41Sopenharmony_ci this[EX].linkpath = this[META].replace(/\0.*/, '') 3401cb0ef41Sopenharmony_ci break 3411cb0ef41Sopenharmony_ci 3421cb0ef41Sopenharmony_ci /* istanbul ignore next */ 3431cb0ef41Sopenharmony_ci default: throw new Error('unknown meta: ' + entry.type) 3441cb0ef41Sopenharmony_ci } 3451cb0ef41Sopenharmony_ci } 3461cb0ef41Sopenharmony_ci 3471cb0ef41Sopenharmony_ci abort (error) { 3481cb0ef41Sopenharmony_ci this[ABORTED] = true 3491cb0ef41Sopenharmony_ci this.emit('abort', error) 3501cb0ef41Sopenharmony_ci // always throws, even in non-strict mode 3511cb0ef41Sopenharmony_ci this.warn('TAR_ABORT', error, { recoverable: false }) 3521cb0ef41Sopenharmony_ci } 3531cb0ef41Sopenharmony_ci 3541cb0ef41Sopenharmony_ci write (chunk) { 3551cb0ef41Sopenharmony_ci if (this[ABORTED]) { 3561cb0ef41Sopenharmony_ci return 3571cb0ef41Sopenharmony_ci } 3581cb0ef41Sopenharmony_ci 3591cb0ef41Sopenharmony_ci // first write, might be gzipped 3601cb0ef41Sopenharmony_ci const needSniff = this[UNZIP] === null || 3611cb0ef41Sopenharmony_ci this.brotli === undefined && this[UNZIP] === false 3621cb0ef41Sopenharmony_ci if (needSniff && chunk) { 3631cb0ef41Sopenharmony_ci if (this[BUFFER]) { 3641cb0ef41Sopenharmony_ci chunk = Buffer.concat([this[BUFFER], chunk]) 3651cb0ef41Sopenharmony_ci this[BUFFER] = null 3661cb0ef41Sopenharmony_ci } 3671cb0ef41Sopenharmony_ci if (chunk.length < gzipHeader.length) { 3681cb0ef41Sopenharmony_ci this[BUFFER] = chunk 3691cb0ef41Sopenharmony_ci return true 3701cb0ef41Sopenharmony_ci } 3711cb0ef41Sopenharmony_ci 3721cb0ef41Sopenharmony_ci // look for gzip header 3731cb0ef41Sopenharmony_ci for (let i = 0; this[UNZIP] === null && i < gzipHeader.length; i++) { 3741cb0ef41Sopenharmony_ci if (chunk[i] !== gzipHeader[i]) { 3751cb0ef41Sopenharmony_ci this[UNZIP] = false 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci } 3781cb0ef41Sopenharmony_ci 3791cb0ef41Sopenharmony_ci const maybeBrotli = this.brotli === undefined 3801cb0ef41Sopenharmony_ci if (this[UNZIP] === false && maybeBrotli) { 3811cb0ef41Sopenharmony_ci // read the first header to see if it's a valid tar file. If so, 3821cb0ef41Sopenharmony_ci // we can safely assume that it's not actually brotli, despite the 3831cb0ef41Sopenharmony_ci // .tbr or .tar.br file extension. 3841cb0ef41Sopenharmony_ci // if we ended before getting a full chunk, yes, def brotli 3851cb0ef41Sopenharmony_ci if (chunk.length < 512) { 3861cb0ef41Sopenharmony_ci if (this[ENDED]) { 3871cb0ef41Sopenharmony_ci this.brotli = true 3881cb0ef41Sopenharmony_ci } else { 3891cb0ef41Sopenharmony_ci this[BUFFER] = chunk 3901cb0ef41Sopenharmony_ci return true 3911cb0ef41Sopenharmony_ci } 3921cb0ef41Sopenharmony_ci } else { 3931cb0ef41Sopenharmony_ci // if it's tar, it's pretty reliably not brotli, chances of 3941cb0ef41Sopenharmony_ci // that happening are astronomical. 3951cb0ef41Sopenharmony_ci try { 3961cb0ef41Sopenharmony_ci new Header(chunk.slice(0, 512)) 3971cb0ef41Sopenharmony_ci this.brotli = false 3981cb0ef41Sopenharmony_ci } catch (_) { 3991cb0ef41Sopenharmony_ci this.brotli = true 4001cb0ef41Sopenharmony_ci } 4011cb0ef41Sopenharmony_ci } 4021cb0ef41Sopenharmony_ci } 4031cb0ef41Sopenharmony_ci 4041cb0ef41Sopenharmony_ci if (this[UNZIP] === null || (this[UNZIP] === false && this.brotli)) { 4051cb0ef41Sopenharmony_ci const ended = this[ENDED] 4061cb0ef41Sopenharmony_ci this[ENDED] = false 4071cb0ef41Sopenharmony_ci this[UNZIP] = this[UNZIP] === null 4081cb0ef41Sopenharmony_ci ? new zlib.Unzip() 4091cb0ef41Sopenharmony_ci : new zlib.BrotliDecompress() 4101cb0ef41Sopenharmony_ci this[UNZIP].on('data', chunk => this[CONSUMECHUNK](chunk)) 4111cb0ef41Sopenharmony_ci this[UNZIP].on('error', er => this.abort(er)) 4121cb0ef41Sopenharmony_ci this[UNZIP].on('end', _ => { 4131cb0ef41Sopenharmony_ci this[ENDED] = true 4141cb0ef41Sopenharmony_ci this[CONSUMECHUNK]() 4151cb0ef41Sopenharmony_ci }) 4161cb0ef41Sopenharmony_ci this[WRITING] = true 4171cb0ef41Sopenharmony_ci const ret = this[UNZIP][ended ? 'end' : 'write'](chunk) 4181cb0ef41Sopenharmony_ci this[WRITING] = false 4191cb0ef41Sopenharmony_ci return ret 4201cb0ef41Sopenharmony_ci } 4211cb0ef41Sopenharmony_ci } 4221cb0ef41Sopenharmony_ci 4231cb0ef41Sopenharmony_ci this[WRITING] = true 4241cb0ef41Sopenharmony_ci if (this[UNZIP]) { 4251cb0ef41Sopenharmony_ci this[UNZIP].write(chunk) 4261cb0ef41Sopenharmony_ci } else { 4271cb0ef41Sopenharmony_ci this[CONSUMECHUNK](chunk) 4281cb0ef41Sopenharmony_ci } 4291cb0ef41Sopenharmony_ci this[WRITING] = false 4301cb0ef41Sopenharmony_ci 4311cb0ef41Sopenharmony_ci // return false if there's a queue, or if the current entry isn't flowing 4321cb0ef41Sopenharmony_ci const ret = 4331cb0ef41Sopenharmony_ci this[QUEUE].length ? false : 4341cb0ef41Sopenharmony_ci this[READENTRY] ? this[READENTRY].flowing : 4351cb0ef41Sopenharmony_ci true 4361cb0ef41Sopenharmony_ci 4371cb0ef41Sopenharmony_ci // if we have no queue, then that means a clogged READENTRY 4381cb0ef41Sopenharmony_ci if (!ret && !this[QUEUE].length) { 4391cb0ef41Sopenharmony_ci this[READENTRY].once('drain', _ => this.emit('drain')) 4401cb0ef41Sopenharmony_ci } 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ci return ret 4431cb0ef41Sopenharmony_ci } 4441cb0ef41Sopenharmony_ci 4451cb0ef41Sopenharmony_ci [BUFFERCONCAT] (c) { 4461cb0ef41Sopenharmony_ci if (c && !this[ABORTED]) { 4471cb0ef41Sopenharmony_ci this[BUFFER] = this[BUFFER] ? Buffer.concat([this[BUFFER], c]) : c 4481cb0ef41Sopenharmony_ci } 4491cb0ef41Sopenharmony_ci } 4501cb0ef41Sopenharmony_ci 4511cb0ef41Sopenharmony_ci [MAYBEEND] () { 4521cb0ef41Sopenharmony_ci if (this[ENDED] && 4531cb0ef41Sopenharmony_ci !this[EMITTEDEND] && 4541cb0ef41Sopenharmony_ci !this[ABORTED] && 4551cb0ef41Sopenharmony_ci !this[CONSUMING]) { 4561cb0ef41Sopenharmony_ci this[EMITTEDEND] = true 4571cb0ef41Sopenharmony_ci const entry = this[WRITEENTRY] 4581cb0ef41Sopenharmony_ci if (entry && entry.blockRemain) { 4591cb0ef41Sopenharmony_ci // truncated, likely a damaged file 4601cb0ef41Sopenharmony_ci const have = this[BUFFER] ? this[BUFFER].length : 0 4611cb0ef41Sopenharmony_ci this.warn('TAR_BAD_ARCHIVE', `Truncated input (needed ${ 4621cb0ef41Sopenharmony_ci entry.blockRemain} more bytes, only ${have} available)`, { entry }) 4631cb0ef41Sopenharmony_ci if (this[BUFFER]) { 4641cb0ef41Sopenharmony_ci entry.write(this[BUFFER]) 4651cb0ef41Sopenharmony_ci } 4661cb0ef41Sopenharmony_ci entry.end() 4671cb0ef41Sopenharmony_ci } 4681cb0ef41Sopenharmony_ci this[EMIT](DONE) 4691cb0ef41Sopenharmony_ci } 4701cb0ef41Sopenharmony_ci } 4711cb0ef41Sopenharmony_ci 4721cb0ef41Sopenharmony_ci [CONSUMECHUNK] (chunk) { 4731cb0ef41Sopenharmony_ci if (this[CONSUMING]) { 4741cb0ef41Sopenharmony_ci this[BUFFERCONCAT](chunk) 4751cb0ef41Sopenharmony_ci } else if (!chunk && !this[BUFFER]) { 4761cb0ef41Sopenharmony_ci this[MAYBEEND]() 4771cb0ef41Sopenharmony_ci } else { 4781cb0ef41Sopenharmony_ci this[CONSUMING] = true 4791cb0ef41Sopenharmony_ci if (this[BUFFER]) { 4801cb0ef41Sopenharmony_ci this[BUFFERCONCAT](chunk) 4811cb0ef41Sopenharmony_ci const c = this[BUFFER] 4821cb0ef41Sopenharmony_ci this[BUFFER] = null 4831cb0ef41Sopenharmony_ci this[CONSUMECHUNKSUB](c) 4841cb0ef41Sopenharmony_ci } else { 4851cb0ef41Sopenharmony_ci this[CONSUMECHUNKSUB](chunk) 4861cb0ef41Sopenharmony_ci } 4871cb0ef41Sopenharmony_ci 4881cb0ef41Sopenharmony_ci while (this[BUFFER] && 4891cb0ef41Sopenharmony_ci this[BUFFER].length >= 512 && 4901cb0ef41Sopenharmony_ci !this[ABORTED] && 4911cb0ef41Sopenharmony_ci !this[SAW_EOF]) { 4921cb0ef41Sopenharmony_ci const c = this[BUFFER] 4931cb0ef41Sopenharmony_ci this[BUFFER] = null 4941cb0ef41Sopenharmony_ci this[CONSUMECHUNKSUB](c) 4951cb0ef41Sopenharmony_ci } 4961cb0ef41Sopenharmony_ci this[CONSUMING] = false 4971cb0ef41Sopenharmony_ci } 4981cb0ef41Sopenharmony_ci 4991cb0ef41Sopenharmony_ci if (!this[BUFFER] || this[ENDED]) { 5001cb0ef41Sopenharmony_ci this[MAYBEEND]() 5011cb0ef41Sopenharmony_ci } 5021cb0ef41Sopenharmony_ci } 5031cb0ef41Sopenharmony_ci 5041cb0ef41Sopenharmony_ci [CONSUMECHUNKSUB] (chunk) { 5051cb0ef41Sopenharmony_ci // we know that we are in CONSUMING mode, so anything written goes into 5061cb0ef41Sopenharmony_ci // the buffer. Advance the position and put any remainder in the buffer. 5071cb0ef41Sopenharmony_ci let position = 0 5081cb0ef41Sopenharmony_ci const length = chunk.length 5091cb0ef41Sopenharmony_ci while (position + 512 <= length && !this[ABORTED] && !this[SAW_EOF]) { 5101cb0ef41Sopenharmony_ci switch (this[STATE]) { 5111cb0ef41Sopenharmony_ci case 'begin': 5121cb0ef41Sopenharmony_ci case 'header': 5131cb0ef41Sopenharmony_ci this[CONSUMEHEADER](chunk, position) 5141cb0ef41Sopenharmony_ci position += 512 5151cb0ef41Sopenharmony_ci break 5161cb0ef41Sopenharmony_ci 5171cb0ef41Sopenharmony_ci case 'ignore': 5181cb0ef41Sopenharmony_ci case 'body': 5191cb0ef41Sopenharmony_ci position += this[CONSUMEBODY](chunk, position) 5201cb0ef41Sopenharmony_ci break 5211cb0ef41Sopenharmony_ci 5221cb0ef41Sopenharmony_ci case 'meta': 5231cb0ef41Sopenharmony_ci position += this[CONSUMEMETA](chunk, position) 5241cb0ef41Sopenharmony_ci break 5251cb0ef41Sopenharmony_ci 5261cb0ef41Sopenharmony_ci /* istanbul ignore next */ 5271cb0ef41Sopenharmony_ci default: 5281cb0ef41Sopenharmony_ci throw new Error('invalid state: ' + this[STATE]) 5291cb0ef41Sopenharmony_ci } 5301cb0ef41Sopenharmony_ci } 5311cb0ef41Sopenharmony_ci 5321cb0ef41Sopenharmony_ci if (position < length) { 5331cb0ef41Sopenharmony_ci if (this[BUFFER]) { 5341cb0ef41Sopenharmony_ci this[BUFFER] = Buffer.concat([chunk.slice(position), this[BUFFER]]) 5351cb0ef41Sopenharmony_ci } else { 5361cb0ef41Sopenharmony_ci this[BUFFER] = chunk.slice(position) 5371cb0ef41Sopenharmony_ci } 5381cb0ef41Sopenharmony_ci } 5391cb0ef41Sopenharmony_ci } 5401cb0ef41Sopenharmony_ci 5411cb0ef41Sopenharmony_ci end (chunk) { 5421cb0ef41Sopenharmony_ci if (!this[ABORTED]) { 5431cb0ef41Sopenharmony_ci if (this[UNZIP]) { 5441cb0ef41Sopenharmony_ci this[UNZIP].end(chunk) 5451cb0ef41Sopenharmony_ci } else { 5461cb0ef41Sopenharmony_ci this[ENDED] = true 5471cb0ef41Sopenharmony_ci if (this.brotli === undefined) chunk = chunk || Buffer.alloc(0) 5481cb0ef41Sopenharmony_ci this.write(chunk) 5491cb0ef41Sopenharmony_ci } 5501cb0ef41Sopenharmony_ci } 5511cb0ef41Sopenharmony_ci } 5521cb0ef41Sopenharmony_ci}) 553