uawdijnntqw1x1x1
IP : 216.73.216.110
Hostname : 6.87.74.97.host.secureserver.net
Kernel : Linux 6.87.74.97.host.secureserver.net 4.18.0-553.83.1.el8_10.x86_64 #1 SMP Mon Nov 10 04:22:44 EST 2025 x86_64
Disable Function : None :)
OS : Linux
PATH:
/
home
/
emeraadmin
/
www
/
js
/
..
/
node_modules
/
isobject
/
..
/
d3-array
/
..
/
function-bind
/
..
/
..
/
4d695
/
tar.tar
/
/
package.json000064400000003443151701414140007034 0ustar00{ "_id": "tar@6.2.1", "_inBundle": true, "_location": "/npm/tar", "_phantomChildren": { "yallist": "4.0.0" }, "_requiredBy": [ "/npm", "/npm/cacache", "/npm/libnpmdiff", "/npm/node-gyp", "/npm/pacote" ], "author": { "name": "GitHub Inc." }, "bugs": { "url": "https://github.com/isaacs/node-tar/issues" }, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "description": "tar for node", "devDependencies": { "@npmcli/eslint-config": "^4.0.0", "@npmcli/template-oss": "4.11.0", "chmodr": "^1.2.0", "end-of-stream": "^1.4.3", "events-to-array": "^2.0.3", "mutate-fs": "^2.1.1", "nock": "^13.2.9", "rimraf": "^3.0.2", "tap": "^16.0.1" }, "engines": { "node": ">=10" }, "files": [ "bin/", "lib/", "index.js" ], "homepage": "https://github.com/isaacs/node-tar#readme", "license": "ISC", "name": "tar", "repository": { "type": "git", "url": "git+https://github.com/isaacs/node-tar.git" }, "scripts": { "genparse": "node scripts/generate-parse-fixtures.js", "snap": "tap", "test": "tap" }, "tap": { "coverage-map": "map.js", "timeout": 0, "nyc-arg": [ "--exclude", "tap-snapshots/**" ] }, "templateOSS": { "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", "version": "4.11.0", "content": "scripts/template-oss", "engines": ">=10", "distPaths": [ "index.js" ], "allowPaths": [ "/index.js" ], "ciVersions": [ "10.x", "12.x", "14.x", "16.x", "18.x" ] }, "version": "6.2.1" } node_modules/minipass/package.json000064400000004025151701414140013331 0ustar00{ "_id": "minipass@5.0.0", "_inBundle": true, "_location": "/npm/tar/minipass", "_phantomChildren": {}, "_requiredBy": [ "/npm/tar" ], "author": { "name": "Isaac Z. Schlueter", "email": "i@izs.me", "url": "http://blog.izs.me/" }, "bugs": { "url": "https://github.com/isaacs/minipass/issues" }, "description": "minimal implementation of a PassThrough stream", "devDependencies": { "@types/node": "^17.0.41", "end-of-stream": "^1.4.0", "node-abort-controller": "^3.1.1", "prettier": "^2.6.2", "tap": "^16.2.0", "through2": "^2.0.3", "ts-node": "^10.8.1", "typedoc": "^0.23.24", "typescript": "^4.7.3" }, "engines": { "node": ">=8" }, "exports": { ".": { "import": { "types": "./index.d.ts", "default": "./index.mjs" }, "require": { "types": "./index.d.ts", "default": "./index.js" } }, "./package.json": "./package.json" }, "files": [ "index.d.ts", "index.js", "index.mjs" ], "homepage": "https://github.com/isaacs/minipass#readme", "keywords": [ "passthrough", "stream" ], "license": "ISC", "main": "./index.js", "module": "./index.mjs", "name": "minipass", "prettier": { "semi": false, "printWidth": 80, "tabWidth": 2, "useTabs": false, "singleQuote": true, "jsxSingleQuote": false, "bracketSameLine": true, "arrowParens": "avoid", "endOfLine": "lf" }, "repository": { "type": "git", "url": "git+https://github.com/isaacs/minipass.git" }, "scripts": { "format": "prettier --write . --loglevel warn", "postpublish": "git push origin --follow-tags", "postversion": "npm publish", "prepare": "node ./scripts/transpile-to-esm.js", "presnap": "npm run prepare", "pretest": "npm run prepare", "preversion": "npm test", "snap": "tap", "test": "tap", "typedoc": "typedoc ./index.d.ts" }, "tap": { "check-coverage": true }, "types": "./index.d.ts", "version": "5.0.0" } node_modules/minipass/LICENSE000064400000001423151701414140012047 0ustar00The ISC License Copyright (c) 2017-2023 npm, Inc., Isaac Z. Schlueter, and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. node_modules/minipass/index.js000064400000044167151701414140012523 0ustar00'use strict' const proc = typeof process === 'object' && process ? process : { stdout: null, stderr: null, } const EE = require('events') const Stream = require('stream') const stringdecoder = require('string_decoder') const SD = stringdecoder.StringDecoder const EOF = Symbol('EOF') const MAYBE_EMIT_END = Symbol('maybeEmitEnd') const EMITTED_END = Symbol('emittedEnd') const EMITTING_END = Symbol('emittingEnd') const EMITTED_ERROR = Symbol('emittedError') const CLOSED = Symbol('closed') const READ = Symbol('read') const FLUSH = Symbol('flush') const FLUSHCHUNK = Symbol('flushChunk') const ENCODING = Symbol('encoding') const DECODER = Symbol('decoder') const FLOWING = Symbol('flowing') const PAUSED = Symbol('paused') const RESUME = Symbol('resume') const BUFFER = Symbol('buffer') const PIPES = Symbol('pipes') const BUFFERLENGTH = Symbol('bufferLength') const BUFFERPUSH = Symbol('bufferPush') const BUFFERSHIFT = Symbol('bufferShift') const OBJECTMODE = Symbol('objectMode') // internal event when stream is destroyed const DESTROYED = Symbol('destroyed') // internal event when stream has an error const ERROR = Symbol('error') const EMITDATA = Symbol('emitData') const EMITEND = Symbol('emitEnd') const EMITEND2 = Symbol('emitEnd2') const ASYNC = Symbol('async') const ABORT = Symbol('abort') const ABORTED = Symbol('aborted') const SIGNAL = Symbol('signal') const defer = fn => Promise.resolve().then(fn) // TODO remove when Node v8 support drops const doIter = global._MP_NO_ITERATOR_SYMBOLS_ !== '1' const ASYNCITERATOR = (doIter && Symbol.asyncIterator) || Symbol('asyncIterator not implemented') const ITERATOR = (doIter && Symbol.iterator) || Symbol('iterator not implemented') // events that mean 'the stream is over' // these are treated specially, and re-emitted // if they are listened for after emitting. const isEndish = ev => ev === 'end' || ev === 'finish' || ev === 'prefinish' const isArrayBuffer = b => b instanceof ArrayBuffer || (typeof b === 'object' && b.constructor && b.constructor.name === 'ArrayBuffer' && b.byteLength >= 0) const isArrayBufferView = b => !Buffer.isBuffer(b) && ArrayBuffer.isView(b) class Pipe { constructor(src, dest, opts) { this.src = src this.dest = dest this.opts = opts this.ondrain = () => src[RESUME]() dest.on('drain', this.ondrain) } unpipe() { this.dest.removeListener('drain', this.ondrain) } // istanbul ignore next - only here for the prototype proxyErrors() {} end() { this.unpipe() if (this.opts.end) this.dest.end() } } class PipeProxyErrors extends Pipe { unpipe() { this.src.removeListener('error', this.proxyErrors) super.unpipe() } constructor(src, dest, opts) { super(src, dest, opts) this.proxyErrors = er => dest.emit('error', er) src.on('error', this.proxyErrors) } } class Minipass extends Stream { constructor(options) { super() this[FLOWING] = false // whether we're explicitly paused this[PAUSED] = false this[PIPES] = [] this[BUFFER] = [] this[OBJECTMODE] = (options && options.objectMode) || false if (this[OBJECTMODE]) this[ENCODING] = null else this[ENCODING] = (options && options.encoding) || null if (this[ENCODING] === 'buffer') this[ENCODING] = null this[ASYNC] = (options && !!options.async) || false this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null this[EOF] = false this[EMITTED_END] = false this[EMITTING_END] = false this[CLOSED] = false this[EMITTED_ERROR] = null this.writable = true this.readable = true this[BUFFERLENGTH] = 0 this[DESTROYED] = false if (options && options.debugExposeBuffer === true) { Object.defineProperty(this, 'buffer', { get: () => this[BUFFER] }) } if (options && options.debugExposePipes === true) { Object.defineProperty(this, 'pipes', { get: () => this[PIPES] }) } this[SIGNAL] = options && options.signal this[ABORTED] = false if (this[SIGNAL]) { this[SIGNAL].addEventListener('abort', () => this[ABORT]()) if (this[SIGNAL].aborted) { this[ABORT]() } } } get bufferLength() { return this[BUFFERLENGTH] } get encoding() { return this[ENCODING] } set encoding(enc) { if (this[OBJECTMODE]) throw new Error('cannot set encoding in objectMode') if ( this[ENCODING] && enc !== this[ENCODING] && ((this[DECODER] && this[DECODER].lastNeed) || this[BUFFERLENGTH]) ) throw new Error('cannot change encoding') if (this[ENCODING] !== enc) { this[DECODER] = enc ? new SD(enc) : null if (this[BUFFER].length) this[BUFFER] = this[BUFFER].map(chunk => this[DECODER].write(chunk)) } this[ENCODING] = enc } setEncoding(enc) { this.encoding = enc } get objectMode() { return this[OBJECTMODE] } set objectMode(om) { this[OBJECTMODE] = this[OBJECTMODE] || !!om } get ['async']() { return this[ASYNC] } set ['async'](a) { this[ASYNC] = this[ASYNC] || !!a } // drop everything and get out of the flow completely [ABORT]() { this[ABORTED] = true this.emit('abort', this[SIGNAL].reason) this.destroy(this[SIGNAL].reason) } get aborted() { return this[ABORTED] } set aborted(_) {} write(chunk, encoding, cb) { if (this[ABORTED]) return false if (this[EOF]) throw new Error('write after end') if (this[DESTROYED]) { this.emit( 'error', Object.assign( new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' } ) ) return true } if (typeof encoding === 'function') (cb = encoding), (encoding = 'utf8') if (!encoding) encoding = 'utf8' const fn = this[ASYNC] ? defer : f => f() // convert array buffers and typed array views into buffers // at some point in the future, we may want to do the opposite! // leave strings and buffers as-is // anything else switches us into object mode if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) { if (isArrayBufferView(chunk)) chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) else if (isArrayBuffer(chunk)) chunk = Buffer.from(chunk) else if (typeof chunk !== 'string') // use the setter so we throw if we have encoding set this.objectMode = true } // handle object mode up front, since it's simpler // this yields better performance, fewer checks later. if (this[OBJECTMODE]) { /* istanbul ignore if - maybe impossible? */ if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // at this point the chunk is a buffer or string // don't buffer it up or send it to the decoder if (!chunk.length) { if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // fast-path writing strings of same encoding to a stream with // an empty buffer, skipping the buffer/decoder dance if ( typeof chunk === 'string' && // unless it is a string already ready for us to use !(encoding === this[ENCODING] && !this[DECODER].lastNeed) ) { chunk = Buffer.from(chunk, encoding) } if (Buffer.isBuffer(chunk) && this[ENCODING]) chunk = this[DECODER].write(chunk) // Note: flushing CAN potentially switch us into not-flowing mode if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } read(n) { if (this[DESTROYED]) return null if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) { this[MAYBE_EMIT_END]() return null } if (this[OBJECTMODE]) n = null if (this[BUFFER].length > 1 && !this[OBJECTMODE]) { if (this.encoding) this[BUFFER] = [this[BUFFER].join('')] else this[BUFFER] = [Buffer.concat(this[BUFFER], this[BUFFERLENGTH])] } const ret = this[READ](n || null, this[BUFFER][0]) this[MAYBE_EMIT_END]() return ret } [READ](n, chunk) { if (n === chunk.length || n === null) this[BUFFERSHIFT]() else { this[BUFFER][0] = chunk.slice(n) chunk = chunk.slice(0, n) this[BUFFERLENGTH] -= n } this.emit('data', chunk) if (!this[BUFFER].length && !this[EOF]) this.emit('drain') return chunk } end(chunk, encoding, cb) { if (typeof chunk === 'function') (cb = chunk), (chunk = null) if (typeof encoding === 'function') (cb = encoding), (encoding = 'utf8') if (chunk) this.write(chunk, encoding) if (cb) this.once('end', cb) this[EOF] = true this.writable = false // if we haven't written anything, then go ahead and emit, // even if we're not reading. // we'll re-emit if a new 'end' listener is added anyway. // This makes MP more suitable to write-only use cases. if (this.flowing || !this[PAUSED]) this[MAYBE_EMIT_END]() return this } // don't let the internal resume be overwritten [RESUME]() { if (this[DESTROYED]) return this[PAUSED] = false this[FLOWING] = true this.emit('resume') if (this[BUFFER].length) this[FLUSH]() else if (this[EOF]) this[MAYBE_EMIT_END]() else this.emit('drain') } resume() { return this[RESUME]() } pause() { this[FLOWING] = false this[PAUSED] = true } get destroyed() { return this[DESTROYED] } get flowing() { return this[FLOWING] } get paused() { return this[PAUSED] } [BUFFERPUSH](chunk) { if (this[OBJECTMODE]) this[BUFFERLENGTH] += 1 else this[BUFFERLENGTH] += chunk.length this[BUFFER].push(chunk) } [BUFFERSHIFT]() { if (this[OBJECTMODE]) this[BUFFERLENGTH] -= 1 else this[BUFFERLENGTH] -= this[BUFFER][0].length return this[BUFFER].shift() } [FLUSH](noDrain) { do {} while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && this[BUFFER].length) if (!noDrain && !this[BUFFER].length && !this[EOF]) this.emit('drain') } [FLUSHCHUNK](chunk) { this.emit('data', chunk) return this.flowing } pipe(dest, opts) { if (this[DESTROYED]) return const ended = this[EMITTED_END] opts = opts || {} if (dest === proc.stdout || dest === proc.stderr) opts.end = false else opts.end = opts.end !== false opts.proxyErrors = !!opts.proxyErrors // piping an ended stream ends immediately if (ended) { if (opts.end) dest.end() } else { this[PIPES].push( !opts.proxyErrors ? new Pipe(this, dest, opts) : new PipeProxyErrors(this, dest, opts) ) if (this[ASYNC]) defer(() => this[RESUME]()) else this[RESUME]() } return dest } unpipe(dest) { const p = this[PIPES].find(p => p.dest === dest) if (p) { this[PIPES].splice(this[PIPES].indexOf(p), 1) p.unpipe() } } addListener(ev, fn) { return this.on(ev, fn) } on(ev, fn) { const ret = super.on(ev, fn) if (ev === 'data' && !this[PIPES].length && !this.flowing) this[RESUME]() else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) super.emit('readable') else if (isEndish(ev) && this[EMITTED_END]) { super.emit(ev) this.removeAllListeners(ev) } else if (ev === 'error' && this[EMITTED_ERROR]) { if (this[ASYNC]) defer(() => fn.call(this, this[EMITTED_ERROR])) else fn.call(this, this[EMITTED_ERROR]) } return ret } get emittedEnd() { return this[EMITTED_END] } [MAYBE_EMIT_END]() { if ( !this[EMITTING_END] && !this[EMITTED_END] && !this[DESTROYED] && this[BUFFER].length === 0 && this[EOF] ) { this[EMITTING_END] = true this.emit('end') this.emit('prefinish') this.emit('finish') if (this[CLOSED]) this.emit('close') this[EMITTING_END] = false } } emit(ev, data, ...extra) { // error and close are only events allowed after calling destroy() if (ev !== 'error' && ev !== 'close' && ev !== DESTROYED && this[DESTROYED]) return else if (ev === 'data') { return !this[OBJECTMODE] && !data ? false : this[ASYNC] ? defer(() => this[EMITDATA](data)) : this[EMITDATA](data) } else if (ev === 'end') { return this[EMITEND]() } else if (ev === 'close') { this[CLOSED] = true // don't emit close before 'end' and 'finish' if (!this[EMITTED_END] && !this[DESTROYED]) return const ret = super.emit('close') this.removeAllListeners('close') return ret } else if (ev === 'error') { this[EMITTED_ERROR] = data super.emit(ERROR, data) const ret = !this[SIGNAL] || this.listeners('error').length ? super.emit('error', data) : false this[MAYBE_EMIT_END]() return ret } else if (ev === 'resume') { const ret = super.emit('resume') this[MAYBE_EMIT_END]() return ret } else if (ev === 'finish' || ev === 'prefinish') { const ret = super.emit(ev) this.removeAllListeners(ev) return ret } // Some other unknown event const ret = super.emit(ev, data, ...extra) this[MAYBE_EMIT_END]() return ret } [EMITDATA](data) { for (const p of this[PIPES]) { if (p.dest.write(data) === false) this.pause() } const ret = super.emit('data', data) this[MAYBE_EMIT_END]() return ret } [EMITEND]() { if (this[EMITTED_END]) return this[EMITTED_END] = true this.readable = false if (this[ASYNC]) defer(() => this[EMITEND2]()) else this[EMITEND2]() } [EMITEND2]() { if (this[DECODER]) { const data = this[DECODER].end() if (data) { for (const p of this[PIPES]) { p.dest.write(data) } super.emit('data', data) } } for (const p of this[PIPES]) { p.end() } const ret = super.emit('end') this.removeAllListeners('end') return ret } // const all = await stream.collect() collect() { const buf = [] if (!this[OBJECTMODE]) buf.dataLength = 0 // set the promise first, in case an error is raised // by triggering the flow here. const p = this.promise() this.on('data', c => { buf.push(c) if (!this[OBJECTMODE]) buf.dataLength += c.length }) return p.then(() => buf) } // const data = await stream.concat() concat() { return this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this.collect().then(buf => this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this[ENCODING] ? buf.join('') : Buffer.concat(buf, buf.dataLength) ) } // stream.promise().then(() => done, er => emitted error) promise() { return new Promise((resolve, reject) => { this.on(DESTROYED, () => reject(new Error('stream destroyed'))) this.on('error', er => reject(er)) this.on('end', () => resolve()) }) } // for await (let chunk of stream) [ASYNCITERATOR]() { let stopped = false const stop = () => { this.pause() stopped = true return Promise.resolve({ done: true }) } const next = () => { if (stopped) return stop() const res = this.read() if (res !== null) return Promise.resolve({ done: false, value: res }) if (this[EOF]) return stop() let resolve = null let reject = null const onerr = er => { this.removeListener('data', ondata) this.removeListener('end', onend) this.removeListener(DESTROYED, ondestroy) stop() reject(er) } const ondata = value => { this.removeListener('error', onerr) this.removeListener('end', onend) this.removeListener(DESTROYED, ondestroy) this.pause() resolve({ value: value, done: !!this[EOF] }) } const onend = () => { this.removeListener('error', onerr) this.removeListener('data', ondata) this.removeListener(DESTROYED, ondestroy) stop() resolve({ done: true }) } const ondestroy = () => onerr(new Error('stream destroyed')) return new Promise((res, rej) => { reject = rej resolve = res this.once(DESTROYED, ondestroy) this.once('error', onerr) this.once('end', onend) this.once('data', ondata) }) } return { next, throw: stop, return: stop, [ASYNCITERATOR]() { return this }, } } // for (let chunk of stream) [ITERATOR]() { let stopped = false const stop = () => { this.pause() this.removeListener(ERROR, stop) this.removeListener(DESTROYED, stop) this.removeListener('end', stop) stopped = true return { done: true } } const next = () => { if (stopped) return stop() const value = this.read() return value === null ? stop() : { value } } this.once('end', stop) this.once(ERROR, stop) this.once(DESTROYED, stop) return { next, throw: stop, return: stop, [ITERATOR]() { return this }, } } destroy(er) { if (this[DESTROYED]) { if (er) this.emit('error', er) else this.emit(DESTROYED) return this } this[DESTROYED] = true // throw away all buffered data, it's never coming out this[BUFFER].length = 0 this[BUFFERLENGTH] = 0 if (typeof this.close === 'function' && !this[CLOSED]) this.close() if (er) this.emit('error', er) // if no error to emit, still reject pending promises else this.emit(DESTROYED) return this } static isStream(s) { return ( !!s && (s instanceof Minipass || s instanceof Stream || (s instanceof EE && // readable (typeof s.pipe === 'function' || // writable (typeof s.write === 'function' && typeof s.end === 'function')))) ) } } exports.Minipass = Minipass node_modules/minipass/index.mjs000064400000044124151701414140012671 0ustar00'use strict' const proc = typeof process === 'object' && process ? process : { stdout: null, stderr: null, } import EE from 'events' import Stream from 'stream' import stringdecoder from 'string_decoder' const SD = stringdecoder.StringDecoder const EOF = Symbol('EOF') const MAYBE_EMIT_END = Symbol('maybeEmitEnd') const EMITTED_END = Symbol('emittedEnd') const EMITTING_END = Symbol('emittingEnd') const EMITTED_ERROR = Symbol('emittedError') const CLOSED = Symbol('closed') const READ = Symbol('read') const FLUSH = Symbol('flush') const FLUSHCHUNK = Symbol('flushChunk') const ENCODING = Symbol('encoding') const DECODER = Symbol('decoder') const FLOWING = Symbol('flowing') const PAUSED = Symbol('paused') const RESUME = Symbol('resume') const BUFFER = Symbol('buffer') const PIPES = Symbol('pipes') const BUFFERLENGTH = Symbol('bufferLength') const BUFFERPUSH = Symbol('bufferPush') const BUFFERSHIFT = Symbol('bufferShift') const OBJECTMODE = Symbol('objectMode') // internal event when stream is destroyed const DESTROYED = Symbol('destroyed') // internal event when stream has an error const ERROR = Symbol('error') const EMITDATA = Symbol('emitData') const EMITEND = Symbol('emitEnd') const EMITEND2 = Symbol('emitEnd2') const ASYNC = Symbol('async') const ABORT = Symbol('abort') const ABORTED = Symbol('aborted') const SIGNAL = Symbol('signal') const defer = fn => Promise.resolve().then(fn) // TODO remove when Node v8 support drops const doIter = global._MP_NO_ITERATOR_SYMBOLS_ !== '1' const ASYNCITERATOR = (doIter && Symbol.asyncIterator) || Symbol('asyncIterator not implemented') const ITERATOR = (doIter && Symbol.iterator) || Symbol('iterator not implemented') // events that mean 'the stream is over' // these are treated specially, and re-emitted // if they are listened for after emitting. const isEndish = ev => ev === 'end' || ev === 'finish' || ev === 'prefinish' const isArrayBuffer = b => b instanceof ArrayBuffer || (typeof b === 'object' && b.constructor && b.constructor.name === 'ArrayBuffer' && b.byteLength >= 0) const isArrayBufferView = b => !Buffer.isBuffer(b) && ArrayBuffer.isView(b) class Pipe { constructor(src, dest, opts) { this.src = src this.dest = dest this.opts = opts this.ondrain = () => src[RESUME]() dest.on('drain', this.ondrain) } unpipe() { this.dest.removeListener('drain', this.ondrain) } // istanbul ignore next - only here for the prototype proxyErrors() {} end() { this.unpipe() if (this.opts.end) this.dest.end() } } class PipeProxyErrors extends Pipe { unpipe() { this.src.removeListener('error', this.proxyErrors) super.unpipe() } constructor(src, dest, opts) { super(src, dest, opts) this.proxyErrors = er => dest.emit('error', er) src.on('error', this.proxyErrors) } } export class Minipass extends Stream { constructor(options) { super() this[FLOWING] = false // whether we're explicitly paused this[PAUSED] = false this[PIPES] = [] this[BUFFER] = [] this[OBJECTMODE] = (options && options.objectMode) || false if (this[OBJECTMODE]) this[ENCODING] = null else this[ENCODING] = (options && options.encoding) || null if (this[ENCODING] === 'buffer') this[ENCODING] = null this[ASYNC] = (options && !!options.async) || false this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null this[EOF] = false this[EMITTED_END] = false this[EMITTING_END] = false this[CLOSED] = false this[EMITTED_ERROR] = null this.writable = true this.readable = true this[BUFFERLENGTH] = 0 this[DESTROYED] = false if (options && options.debugExposeBuffer === true) { Object.defineProperty(this, 'buffer', { get: () => this[BUFFER] }) } if (options && options.debugExposePipes === true) { Object.defineProperty(this, 'pipes', { get: () => this[PIPES] }) } this[SIGNAL] = options && options.signal this[ABORTED] = false if (this[SIGNAL]) { this[SIGNAL].addEventListener('abort', () => this[ABORT]()) if (this[SIGNAL].aborted) { this[ABORT]() } } } get bufferLength() { return this[BUFFERLENGTH] } get encoding() { return this[ENCODING] } set encoding(enc) { if (this[OBJECTMODE]) throw new Error('cannot set encoding in objectMode') if ( this[ENCODING] && enc !== this[ENCODING] && ((this[DECODER] && this[DECODER].lastNeed) || this[BUFFERLENGTH]) ) throw new Error('cannot change encoding') if (this[ENCODING] !== enc) { this[DECODER] = enc ? new SD(enc) : null if (this[BUFFER].length) this[BUFFER] = this[BUFFER].map(chunk => this[DECODER].write(chunk)) } this[ENCODING] = enc } setEncoding(enc) { this.encoding = enc } get objectMode() { return this[OBJECTMODE] } set objectMode(om) { this[OBJECTMODE] = this[OBJECTMODE] || !!om } get ['async']() { return this[ASYNC] } set ['async'](a) { this[ASYNC] = this[ASYNC] || !!a } // drop everything and get out of the flow completely [ABORT]() { this[ABORTED] = true this.emit('abort', this[SIGNAL].reason) this.destroy(this[SIGNAL].reason) } get aborted() { return this[ABORTED] } set aborted(_) {} write(chunk, encoding, cb) { if (this[ABORTED]) return false if (this[EOF]) throw new Error('write after end') if (this[DESTROYED]) { this.emit( 'error', Object.assign( new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' } ) ) return true } if (typeof encoding === 'function') (cb = encoding), (encoding = 'utf8') if (!encoding) encoding = 'utf8' const fn = this[ASYNC] ? defer : f => f() // convert array buffers and typed array views into buffers // at some point in the future, we may want to do the opposite! // leave strings and buffers as-is // anything else switches us into object mode if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) { if (isArrayBufferView(chunk)) chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) else if (isArrayBuffer(chunk)) chunk = Buffer.from(chunk) else if (typeof chunk !== 'string') // use the setter so we throw if we have encoding set this.objectMode = true } // handle object mode up front, since it's simpler // this yields better performance, fewer checks later. if (this[OBJECTMODE]) { /* istanbul ignore if - maybe impossible? */ if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // at this point the chunk is a buffer or string // don't buffer it up or send it to the decoder if (!chunk.length) { if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // fast-path writing strings of same encoding to a stream with // an empty buffer, skipping the buffer/decoder dance if ( typeof chunk === 'string' && // unless it is a string already ready for us to use !(encoding === this[ENCODING] && !this[DECODER].lastNeed) ) { chunk = Buffer.from(chunk, encoding) } if (Buffer.isBuffer(chunk) && this[ENCODING]) chunk = this[DECODER].write(chunk) // Note: flushing CAN potentially switch us into not-flowing mode if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } read(n) { if (this[DESTROYED]) return null if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) { this[MAYBE_EMIT_END]() return null } if (this[OBJECTMODE]) n = null if (this[BUFFER].length > 1 && !this[OBJECTMODE]) { if (this.encoding) this[BUFFER] = [this[BUFFER].join('')] else this[BUFFER] = [Buffer.concat(this[BUFFER], this[BUFFERLENGTH])] } const ret = this[READ](n || null, this[BUFFER][0]) this[MAYBE_EMIT_END]() return ret } [READ](n, chunk) { if (n === chunk.length || n === null) this[BUFFERSHIFT]() else { this[BUFFER][0] = chunk.slice(n) chunk = chunk.slice(0, n) this[BUFFERLENGTH] -= n } this.emit('data', chunk) if (!this[BUFFER].length && !this[EOF]) this.emit('drain') return chunk } end(chunk, encoding, cb) { if (typeof chunk === 'function') (cb = chunk), (chunk = null) if (typeof encoding === 'function') (cb = encoding), (encoding = 'utf8') if (chunk) this.write(chunk, encoding) if (cb) this.once('end', cb) this[EOF] = true this.writable = false // if we haven't written anything, then go ahead and emit, // even if we're not reading. // we'll re-emit if a new 'end' listener is added anyway. // This makes MP more suitable to write-only use cases. if (this.flowing || !this[PAUSED]) this[MAYBE_EMIT_END]() return this } // don't let the internal resume be overwritten [RESUME]() { if (this[DESTROYED]) return this[PAUSED] = false this[FLOWING] = true this.emit('resume') if (this[BUFFER].length) this[FLUSH]() else if (this[EOF]) this[MAYBE_EMIT_END]() else this.emit('drain') } resume() { return this[RESUME]() } pause() { this[FLOWING] = false this[PAUSED] = true } get destroyed() { return this[DESTROYED] } get flowing() { return this[FLOWING] } get paused() { return this[PAUSED] } [BUFFERPUSH](chunk) { if (this[OBJECTMODE]) this[BUFFERLENGTH] += 1 else this[BUFFERLENGTH] += chunk.length this[BUFFER].push(chunk) } [BUFFERSHIFT]() { if (this[OBJECTMODE]) this[BUFFERLENGTH] -= 1 else this[BUFFERLENGTH] -= this[BUFFER][0].length return this[BUFFER].shift() } [FLUSH](noDrain) { do {} while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && this[BUFFER].length) if (!noDrain && !this[BUFFER].length && !this[EOF]) this.emit('drain') } [FLUSHCHUNK](chunk) { this.emit('data', chunk) return this.flowing } pipe(dest, opts) { if (this[DESTROYED]) return const ended = this[EMITTED_END] opts = opts || {} if (dest === proc.stdout || dest === proc.stderr) opts.end = false else opts.end = opts.end !== false opts.proxyErrors = !!opts.proxyErrors // piping an ended stream ends immediately if (ended) { if (opts.end) dest.end() } else { this[PIPES].push( !opts.proxyErrors ? new Pipe(this, dest, opts) : new PipeProxyErrors(this, dest, opts) ) if (this[ASYNC]) defer(() => this[RESUME]()) else this[RESUME]() } return dest } unpipe(dest) { const p = this[PIPES].find(p => p.dest === dest) if (p) { this[PIPES].splice(this[PIPES].indexOf(p), 1) p.unpipe() } } addListener(ev, fn) { return this.on(ev, fn) } on(ev, fn) { const ret = super.on(ev, fn) if (ev === 'data' && !this[PIPES].length && !this.flowing) this[RESUME]() else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) super.emit('readable') else if (isEndish(ev) && this[EMITTED_END]) { super.emit(ev) this.removeAllListeners(ev) } else if (ev === 'error' && this[EMITTED_ERROR]) { if (this[ASYNC]) defer(() => fn.call(this, this[EMITTED_ERROR])) else fn.call(this, this[EMITTED_ERROR]) } return ret } get emittedEnd() { return this[EMITTED_END] } [MAYBE_EMIT_END]() { if ( !this[EMITTING_END] && !this[EMITTED_END] && !this[DESTROYED] && this[BUFFER].length === 0 && this[EOF] ) { this[EMITTING_END] = true this.emit('end') this.emit('prefinish') this.emit('finish') if (this[CLOSED]) this.emit('close') this[EMITTING_END] = false } } emit(ev, data, ...extra) { // error and close are only events allowed after calling destroy() if (ev !== 'error' && ev !== 'close' && ev !== DESTROYED && this[DESTROYED]) return else if (ev === 'data') { return !this[OBJECTMODE] && !data ? false : this[ASYNC] ? defer(() => this[EMITDATA](data)) : this[EMITDATA](data) } else if (ev === 'end') { return this[EMITEND]() } else if (ev === 'close') { this[CLOSED] = true // don't emit close before 'end' and 'finish' if (!this[EMITTED_END] && !this[DESTROYED]) return const ret = super.emit('close') this.removeAllListeners('close') return ret } else if (ev === 'error') { this[EMITTED_ERROR] = data super.emit(ERROR, data) const ret = !this[SIGNAL] || this.listeners('error').length ? super.emit('error', data) : false this[MAYBE_EMIT_END]() return ret } else if (ev === 'resume') { const ret = super.emit('resume') this[MAYBE_EMIT_END]() return ret } else if (ev === 'finish' || ev === 'prefinish') { const ret = super.emit(ev) this.removeAllListeners(ev) return ret } // Some other unknown event const ret = super.emit(ev, data, ...extra) this[MAYBE_EMIT_END]() return ret } [EMITDATA](data) { for (const p of this[PIPES]) { if (p.dest.write(data) === false) this.pause() } const ret = super.emit('data', data) this[MAYBE_EMIT_END]() return ret } [EMITEND]() { if (this[EMITTED_END]) return this[EMITTED_END] = true this.readable = false if (this[ASYNC]) defer(() => this[EMITEND2]()) else this[EMITEND2]() } [EMITEND2]() { if (this[DECODER]) { const data = this[DECODER].end() if (data) { for (const p of this[PIPES]) { p.dest.write(data) } super.emit('data', data) } } for (const p of this[PIPES]) { p.end() } const ret = super.emit('end') this.removeAllListeners('end') return ret } // const all = await stream.collect() collect() { const buf = [] if (!this[OBJECTMODE]) buf.dataLength = 0 // set the promise first, in case an error is raised // by triggering the flow here. const p = this.promise() this.on('data', c => { buf.push(c) if (!this[OBJECTMODE]) buf.dataLength += c.length }) return p.then(() => buf) } // const data = await stream.concat() concat() { return this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this.collect().then(buf => this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this[ENCODING] ? buf.join('') : Buffer.concat(buf, buf.dataLength) ) } // stream.promise().then(() => done, er => emitted error) promise() { return new Promise((resolve, reject) => { this.on(DESTROYED, () => reject(new Error('stream destroyed'))) this.on('error', er => reject(er)) this.on('end', () => resolve()) }) } // for await (let chunk of stream) [ASYNCITERATOR]() { let stopped = false const stop = () => { this.pause() stopped = true return Promise.resolve({ done: true }) } const next = () => { if (stopped) return stop() const res = this.read() if (res !== null) return Promise.resolve({ done: false, value: res }) if (this[EOF]) return stop() let resolve = null let reject = null const onerr = er => { this.removeListener('data', ondata) this.removeListener('end', onend) this.removeListener(DESTROYED, ondestroy) stop() reject(er) } const ondata = value => { this.removeListener('error', onerr) this.removeListener('end', onend) this.removeListener(DESTROYED, ondestroy) this.pause() resolve({ value: value, done: !!this[EOF] }) } const onend = () => { this.removeListener('error', onerr) this.removeListener('data', ondata) this.removeListener(DESTROYED, ondestroy) stop() resolve({ done: true }) } const ondestroy = () => onerr(new Error('stream destroyed')) return new Promise((res, rej) => { reject = rej resolve = res this.once(DESTROYED, ondestroy) this.once('error', onerr) this.once('end', onend) this.once('data', ondata) }) } return { next, throw: stop, return: stop, [ASYNCITERATOR]() { return this }, } } // for (let chunk of stream) [ITERATOR]() { let stopped = false const stop = () => { this.pause() this.removeListener(ERROR, stop) this.removeListener(DESTROYED, stop) this.removeListener('end', stop) stopped = true return { done: true } } const next = () => { if (stopped) return stop() const value = this.read() return value === null ? stop() : { value } } this.once('end', stop) this.once(ERROR, stop) this.once(DESTROYED, stop) return { next, throw: stop, return: stop, [ITERATOR]() { return this }, } } destroy(er) { if (this[DESTROYED]) { if (er) this.emit('error', er) else this.emit(DESTROYED) return this } this[DESTROYED] = true // throw away all buffered data, it's never coming out this[BUFFER].length = 0 this[BUFFERLENGTH] = 0 if (typeof this.close === 'function' && !this[CLOSED]) this.close() if (er) this.emit('error', er) // if no error to emit, still reject pending promises else this.emit(DESTROYED) return this } static isStream(s) { return ( !!s && (s instanceof Minipass || s instanceof Stream || (s instanceof EE && // readable (typeof s.pipe === 'function' || // writable (typeof s.write === 'function' && typeof s.end === 'function')))) ) } } node_modules/fs-minipass/package.json000064400000002102151701414140013731 0ustar00{ "_id": "fs-minipass@2.1.0", "_inBundle": true, "_location": "/npm/tar/fs-minipass", "_phantomChildren": { "yallist": "4.0.0" }, "_requiredBy": [ "/npm/tar" ], "author": { "name": "Isaac Z. Schlueter", "email": "i@izs.me", "url": "http://blog.izs.me/" }, "bugs": { "url": "https://github.com/npm/fs-minipass/issues" }, "dependencies": { "minipass": "^3.0.0" }, "description": "fs read and write streams based on minipass", "devDependencies": { "mutate-fs": "^2.0.1", "tap": "^14.6.4" }, "engines": { "node": ">= 8" }, "files": [ "index.js" ], "homepage": "https://github.com/npm/fs-minipass#readme", "keywords": [], "license": "ISC", "main": "index.js", "name": "fs-minipass", "repository": { "type": "git", "url": "git+https://github.com/npm/fs-minipass.git" }, "scripts": { "postpublish": "git push origin --follow-tags", "postversion": "npm publish", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true }, "version": "2.1.0" } node_modules/fs-minipass/node_modules/minipass/package.json000064400000002777151701414140020253 0ustar00{ "_id": "minipass@3.3.6", "_inBundle": true, "_location": "/npm/tar/fs-minipass/minipass", "_phantomChildren": {}, "_requiredBy": [ "/npm/tar/fs-minipass" ], "author": { "name": "Isaac Z. Schlueter", "email": "i@izs.me", "url": "http://blog.izs.me/" }, "bugs": { "url": "https://github.com/isaacs/minipass/issues" }, "dependencies": { "yallist": "^4.0.0" }, "description": "minimal implementation of a PassThrough stream", "devDependencies": { "@types/node": "^17.0.41", "end-of-stream": "^1.4.0", "prettier": "^2.6.2", "tap": "^16.2.0", "through2": "^2.0.3", "ts-node": "^10.8.1", "typescript": "^4.7.3" }, "engines": { "node": ">=8" }, "files": [ "index.d.ts", "index.js" ], "homepage": "https://github.com/isaacs/minipass#readme", "keywords": [ "passthrough", "stream" ], "license": "ISC", "main": "index.js", "name": "minipass", "prettier": { "semi": false, "printWidth": 80, "tabWidth": 2, "useTabs": false, "singleQuote": true, "jsxSingleQuote": false, "bracketSameLine": true, "arrowParens": "avoid", "endOfLine": "lf" }, "repository": { "type": "git", "url": "git+https://github.com/isaacs/minipass.git" }, "scripts": { "postpublish": "git push origin --follow-tags", "postversion": "npm publish", "preversion": "npm test", "test": "tap" }, "tap": { "check-coverage": true }, "types": "index.d.ts", "version": "3.3.6" } node_modules/fs-minipass/node_modules/minipass/LICENSE000064400000001423151701414140016755 0ustar00The ISC License Copyright (c) 2017-2022 npm, Inc., Isaac Z. Schlueter, and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. node_modules/fs-minipass/node_modules/minipass/index.js000064400000040367151701414140017427 0ustar00'use strict' const proc = typeof process === 'object' && process ? process : { stdout: null, stderr: null, } const EE = require('events') const Stream = require('stream') const SD = require('string_decoder').StringDecoder const EOF = Symbol('EOF') const MAYBE_EMIT_END = Symbol('maybeEmitEnd') const EMITTED_END = Symbol('emittedEnd') const EMITTING_END = Symbol('emittingEnd') const EMITTED_ERROR = Symbol('emittedError') const CLOSED = Symbol('closed') const READ = Symbol('read') const FLUSH = Symbol('flush') const FLUSHCHUNK = Symbol('flushChunk') const ENCODING = Symbol('encoding') const DECODER = Symbol('decoder') const FLOWING = Symbol('flowing') const PAUSED = Symbol('paused') const RESUME = Symbol('resume') const BUFFERLENGTH = Symbol('bufferLength') const BUFFERPUSH = Symbol('bufferPush') const BUFFERSHIFT = Symbol('bufferShift') const OBJECTMODE = Symbol('objectMode') const DESTROYED = Symbol('destroyed') const EMITDATA = Symbol('emitData') const EMITEND = Symbol('emitEnd') const EMITEND2 = Symbol('emitEnd2') const ASYNC = Symbol('async') const defer = fn => Promise.resolve().then(fn) // TODO remove when Node v8 support drops const doIter = global._MP_NO_ITERATOR_SYMBOLS_ !== '1' const ASYNCITERATOR = doIter && Symbol.asyncIterator || Symbol('asyncIterator not implemented') const ITERATOR = doIter && Symbol.iterator || Symbol('iterator not implemented') // events that mean 'the stream is over' // these are treated specially, and re-emitted // if they are listened for after emitting. const isEndish = ev => ev === 'end' || ev === 'finish' || ev === 'prefinish' const isArrayBuffer = b => b instanceof ArrayBuffer || typeof b === 'object' && b.constructor && b.constructor.name === 'ArrayBuffer' && b.byteLength >= 0 const isArrayBufferView = b => !Buffer.isBuffer(b) && ArrayBuffer.isView(b) class Pipe { constructor (src, dest, opts) { this.src = src this.dest = dest this.opts = opts this.ondrain = () => src[RESUME]() dest.on('drain', this.ondrain) } unpipe () { this.dest.removeListener('drain', this.ondrain) } // istanbul ignore next - only here for the prototype proxyErrors () {} end () { this.unpipe() if (this.opts.end) this.dest.end() } } class PipeProxyErrors extends Pipe { unpipe () { this.src.removeListener('error', this.proxyErrors) super.unpipe() } constructor (src, dest, opts) { super(src, dest, opts) this.proxyErrors = er => dest.emit('error', er) src.on('error', this.proxyErrors) } } module.exports = class Minipass extends Stream { constructor (options) { super() this[FLOWING] = false // whether we're explicitly paused this[PAUSED] = false this.pipes = [] this.buffer = [] this[OBJECTMODE] = options && options.objectMode || false if (this[OBJECTMODE]) this[ENCODING] = null else this[ENCODING] = options && options.encoding || null if (this[ENCODING] === 'buffer') this[ENCODING] = null this[ASYNC] = options && !!options.async || false this[DECODER] = this[ENCODING] ? new SD(this[ENCODING]) : null this[EOF] = false this[EMITTED_END] = false this[EMITTING_END] = false this[CLOSED] = false this[EMITTED_ERROR] = null this.writable = true this.readable = true this[BUFFERLENGTH] = 0 this[DESTROYED] = false } get bufferLength () { return this[BUFFERLENGTH] } get encoding () { return this[ENCODING] } set encoding (enc) { if (this[OBJECTMODE]) throw new Error('cannot set encoding in objectMode') if (this[ENCODING] && enc !== this[ENCODING] && (this[DECODER] && this[DECODER].lastNeed || this[BUFFERLENGTH])) throw new Error('cannot change encoding') if (this[ENCODING] !== enc) { this[DECODER] = enc ? new SD(enc) : null if (this.buffer.length) this.buffer = this.buffer.map(chunk => this[DECODER].write(chunk)) } this[ENCODING] = enc } setEncoding (enc) { this.encoding = enc } get objectMode () { return this[OBJECTMODE] } set objectMode (om) { this[OBJECTMODE] = this[OBJECTMODE] || !!om } get ['async'] () { return this[ASYNC] } set ['async'] (a) { this[ASYNC] = this[ASYNC] || !!a } write (chunk, encoding, cb) { if (this[EOF]) throw new Error('write after end') if (this[DESTROYED]) { this.emit('error', Object.assign( new Error('Cannot call write after a stream was destroyed'), { code: 'ERR_STREAM_DESTROYED' } )) return true } if (typeof encoding === 'function') cb = encoding, encoding = 'utf8' if (!encoding) encoding = 'utf8' const fn = this[ASYNC] ? defer : f => f() // convert array buffers and typed array views into buffers // at some point in the future, we may want to do the opposite! // leave strings and buffers as-is // anything else switches us into object mode if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) { if (isArrayBufferView(chunk)) chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) else if (isArrayBuffer(chunk)) chunk = Buffer.from(chunk) else if (typeof chunk !== 'string') // use the setter so we throw if we have encoding set this.objectMode = true } // handle object mode up front, since it's simpler // this yields better performance, fewer checks later. if (this[OBJECTMODE]) { /* istanbul ignore if - maybe impossible? */ if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // at this point the chunk is a buffer or string // don't buffer it up or send it to the decoder if (!chunk.length) { if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } // fast-path writing strings of same encoding to a stream with // an empty buffer, skipping the buffer/decoder dance if (typeof chunk === 'string' && // unless it is a string already ready for us to use !(encoding === this[ENCODING] && !this[DECODER].lastNeed)) { chunk = Buffer.from(chunk, encoding) } if (Buffer.isBuffer(chunk) && this[ENCODING]) chunk = this[DECODER].write(chunk) // Note: flushing CAN potentially switch us into not-flowing mode if (this.flowing && this[BUFFERLENGTH] !== 0) this[FLUSH](true) if (this.flowing) this.emit('data', chunk) else this[BUFFERPUSH](chunk) if (this[BUFFERLENGTH] !== 0) this.emit('readable') if (cb) fn(cb) return this.flowing } read (n) { if (this[DESTROYED]) return null if (this[BUFFERLENGTH] === 0 || n === 0 || n > this[BUFFERLENGTH]) { this[MAYBE_EMIT_END]() return null } if (this[OBJECTMODE]) n = null if (this.buffer.length > 1 && !this[OBJECTMODE]) { if (this.encoding) this.buffer = [this.buffer.join('')] else this.buffer = [Buffer.concat(this.buffer, this[BUFFERLENGTH])] } const ret = this[READ](n || null, this.buffer[0]) this[MAYBE_EMIT_END]() return ret } [READ] (n, chunk) { if (n === chunk.length || n === null) this[BUFFERSHIFT]() else { this.buffer[0] = chunk.slice(n) chunk = chunk.slice(0, n) this[BUFFERLENGTH] -= n } this.emit('data', chunk) if (!this.buffer.length && !this[EOF]) this.emit('drain') return chunk } end (chunk, encoding, cb) { if (typeof chunk === 'function') cb = chunk, chunk = null if (typeof encoding === 'function') cb = encoding, encoding = 'utf8' if (chunk) this.write(chunk, encoding) if (cb) this.once('end', cb) this[EOF] = true this.writable = false // if we haven't written anything, then go ahead and emit, // even if we're not reading. // we'll re-emit if a new 'end' listener is added anyway. // This makes MP more suitable to write-only use cases. if (this.flowing || !this[PAUSED]) this[MAYBE_EMIT_END]() return this } // don't let the internal resume be overwritten [RESUME] () { if (this[DESTROYED]) return this[PAUSED] = false this[FLOWING] = true this.emit('resume') if (this.buffer.length) this[FLUSH]() else if (this[EOF]) this[MAYBE_EMIT_END]() else this.emit('drain') } resume () { return this[RESUME]() } pause () { this[FLOWING] = false this[PAUSED] = true } get destroyed () { return this[DESTROYED] } get flowing () { return this[FLOWING] } get paused () { return this[PAUSED] } [BUFFERPUSH] (chunk) { if (this[OBJECTMODE]) this[BUFFERLENGTH] += 1 else this[BUFFERLENGTH] += chunk.length this.buffer.push(chunk) } [BUFFERSHIFT] () { if (this.buffer.length) { if (this[OBJECTMODE]) this[BUFFERLENGTH] -= 1 else this[BUFFERLENGTH] -= this.buffer[0].length } return this.buffer.shift() } [FLUSH] (noDrain) { do {} while (this[FLUSHCHUNK](this[BUFFERSHIFT]())) if (!noDrain && !this.buffer.length && !this[EOF]) this.emit('drain') } [FLUSHCHUNK] (chunk) { return chunk ? (this.emit('data', chunk), this.flowing) : false } pipe (dest, opts) { if (this[DESTROYED]) return const ended = this[EMITTED_END] opts = opts || {} if (dest === proc.stdout || dest === proc.stderr) opts.end = false else opts.end = opts.end !== false opts.proxyErrors = !!opts.proxyErrors // piping an ended stream ends immediately if (ended) { if (opts.end) dest.end() } else { this.pipes.push(!opts.proxyErrors ? new Pipe(this, dest, opts) : new PipeProxyErrors(this, dest, opts)) if (this[ASYNC]) defer(() => this[RESUME]()) else this[RESUME]() } return dest } unpipe (dest) { const p = this.pipes.find(p => p.dest === dest) if (p) { this.pipes.splice(this.pipes.indexOf(p), 1) p.unpipe() } } addListener (ev, fn) { return this.on(ev, fn) } on (ev, fn) { const ret = super.on(ev, fn) if (ev === 'data' && !this.pipes.length && !this.flowing) this[RESUME]() else if (ev === 'readable' && this[BUFFERLENGTH] !== 0) super.emit('readable') else if (isEndish(ev) && this[EMITTED_END]) { super.emit(ev) this.removeAllListeners(ev) } else if (ev === 'error' && this[EMITTED_ERROR]) { if (this[ASYNC]) defer(() => fn.call(this, this[EMITTED_ERROR])) else fn.call(this, this[EMITTED_ERROR]) } return ret } get emittedEnd () { return this[EMITTED_END] } [MAYBE_EMIT_END] () { if (!this[EMITTING_END] && !this[EMITTED_END] && !this[DESTROYED] && this.buffer.length === 0 && this[EOF]) { this[EMITTING_END] = true this.emit('end') this.emit('prefinish') this.emit('finish') if (this[CLOSED]) this.emit('close') this[EMITTING_END] = false } } emit (ev, data, ...extra) { // error and close are only events allowed after calling destroy() if (ev !== 'error' && ev !== 'close' && ev !== DESTROYED && this[DESTROYED]) return else if (ev === 'data') { return !data ? false : this[ASYNC] ? defer(() => this[EMITDATA](data)) : this[EMITDATA](data) } else if (ev === 'end') { return this[EMITEND]() } else if (ev === 'close') { this[CLOSED] = true // don't emit close before 'end' and 'finish' if (!this[EMITTED_END] && !this[DESTROYED]) return const ret = super.emit('close') this.removeAllListeners('close') return ret } else if (ev === 'error') { this[EMITTED_ERROR] = data const ret = super.emit('error', data) this[MAYBE_EMIT_END]() return ret } else if (ev === 'resume') { const ret = super.emit('resume') this[MAYBE_EMIT_END]() return ret } else if (ev === 'finish' || ev === 'prefinish') { const ret = super.emit(ev) this.removeAllListeners(ev) return ret } // Some other unknown event const ret = super.emit(ev, data, ...extra) this[MAYBE_EMIT_END]() return ret } [EMITDATA] (data) { for (const p of this.pipes) { if (p.dest.write(data) === false) this.pause() } const ret = super.emit('data', data) this[MAYBE_EMIT_END]() return ret } [EMITEND] () { if (this[EMITTED_END]) return this[EMITTED_END] = true this.readable = false if (this[ASYNC]) defer(() => this[EMITEND2]()) else this[EMITEND2]() } [EMITEND2] () { if (this[DECODER]) { const data = this[DECODER].end() if (data) { for (const p of this.pipes) { p.dest.write(data) } super.emit('data', data) } } for (const p of this.pipes) { p.end() } const ret = super.emit('end') this.removeAllListeners('end') return ret } // const all = await stream.collect() collect () { const buf = [] if (!this[OBJECTMODE]) buf.dataLength = 0 // set the promise first, in case an error is raised // by triggering the flow here. const p = this.promise() this.on('data', c => { buf.push(c) if (!this[OBJECTMODE]) buf.dataLength += c.length }) return p.then(() => buf) } // const data = await stream.concat() concat () { return this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this.collect().then(buf => this[OBJECTMODE] ? Promise.reject(new Error('cannot concat in objectMode')) : this[ENCODING] ? buf.join('') : Buffer.concat(buf, buf.dataLength)) } // stream.promise().then(() => done, er => emitted error) promise () { return new Promise((resolve, reject) => { this.on(DESTROYED, () => reject(new Error('stream destroyed'))) this.on('error', er => reject(er)) this.on('end', () => resolve()) }) } // for await (let chunk of stream) [ASYNCITERATOR] () { const next = () => { const res = this.read() if (res !== null) return Promise.resolve({ done: false, value: res }) if (this[EOF]) return Promise.resolve({ done: true }) let resolve = null let reject = null const onerr = er => { this.removeListener('data', ondata) this.removeListener('end', onend) reject(er) } const ondata = value => { this.removeListener('error', onerr) this.removeListener('end', onend) this.pause() resolve({ value: value, done: !!this[EOF] }) } const onend = () => { this.removeListener('error', onerr) this.removeListener('data', ondata) resolve({ done: true }) } const ondestroy = () => onerr(new Error('stream destroyed')) return new Promise((res, rej) => { reject = rej resolve = res this.once(DESTROYED, ondestroy) this.once('error', onerr) this.once('end', onend) this.once('data', ondata) }) } return { next } } // for (let chunk of stream) [ITERATOR] () { const next = () => { const value = this.read() const done = value === null return { value, done } } return { next } } destroy (er) { if (this[DESTROYED]) { if (er) this.emit('error', er) else this.emit(DESTROYED) return this } this[DESTROYED] = true // throw away all buffered data, it's never coming out this.buffer.length = 0 this[BUFFERLENGTH] = 0 if (typeof this.close === 'function' && !this[CLOSED]) this.close() if (er) this.emit('error', er) else // if no error to emit, still reject pending promises this.emit(DESTROYED) return this } static isStream (s) { return !!s && (s instanceof Minipass || s instanceof Stream || s instanceof EE && ( typeof s.pipe === 'function' || // readable (typeof s.write === 'function' && typeof s.end === 'function') // writable )) } } node_modules/fs-minipass/LICENSE000064400000001375151701414140012463 0ustar00The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. node_modules/fs-minipass/index.js000064400000023406151701414140013122 0ustar00'use strict' const MiniPass = require('minipass') const EE = require('events').EventEmitter const fs = require('fs') let writev = fs.writev /* istanbul ignore next */ if (!writev) { // This entire block can be removed if support for earlier than Node.js // 12.9.0 is not needed. const binding = process.binding('fs') const FSReqWrap = binding.FSReqWrap || binding.FSReqCallback writev = (fd, iovec, pos, cb) => { const done = (er, bw) => cb(er, bw, iovec) const req = new FSReqWrap() req.oncomplete = done binding.writeBuffers(fd, iovec, pos, req) } } const _autoClose = Symbol('_autoClose') const _close = Symbol('_close') const _ended = Symbol('_ended') const _fd = Symbol('_fd') const _finished = Symbol('_finished') const _flags = Symbol('_flags') const _flush = Symbol('_flush') const _handleChunk = Symbol('_handleChunk') const _makeBuf = Symbol('_makeBuf') const _mode = Symbol('_mode') const _needDrain = Symbol('_needDrain') const _onerror = Symbol('_onerror') const _onopen = Symbol('_onopen') const _onread = Symbol('_onread') const _onwrite = Symbol('_onwrite') const _open = Symbol('_open') const _path = Symbol('_path') const _pos = Symbol('_pos') const _queue = Symbol('_queue') const _read = Symbol('_read') const _readSize = Symbol('_readSize') const _reading = Symbol('_reading') const _remain = Symbol('_remain') const _size = Symbol('_size') const _write = Symbol('_write') const _writing = Symbol('_writing') const _defaultFlag = Symbol('_defaultFlag') const _errored = Symbol('_errored') class ReadStream extends MiniPass { constructor (path, opt) { opt = opt || {} super(opt) this.readable = true this.writable = false if (typeof path !== 'string') throw new TypeError('path must be a string') this[_errored] = false this[_fd] = typeof opt.fd === 'number' ? opt.fd : null this[_path] = path this[_readSize] = opt.readSize || 16*1024*1024 this[_reading] = false this[_size] = typeof opt.size === 'number' ? opt.size : Infinity this[_remain] = this[_size] this[_autoClose] = typeof opt.autoClose === 'boolean' ? opt.autoClose : true if (typeof this[_fd] === 'number') this[_read]() else this[_open]() } get fd () { return this[_fd] } get path () { return this[_path] } write () { throw new TypeError('this is a readable stream') } end () { throw new TypeError('this is a readable stream') } [_open] () { fs.open(this[_path], 'r', (er, fd) => this[_onopen](er, fd)) } [_onopen] (er, fd) { if (er) this[_onerror](er) else { this[_fd] = fd this.emit('open', fd) this[_read]() } } [_makeBuf] () { return Buffer.allocUnsafe(Math.min(this[_readSize], this[_remain])) } [_read] () { if (!this[_reading]) { this[_reading] = true const buf = this[_makeBuf]() /* istanbul ignore if */ if (buf.length === 0) return process.nextTick(() => this[_onread](null, 0, buf)) fs.read(this[_fd], buf, 0, buf.length, null, (er, br, buf) => this[_onread](er, br, buf)) } } [_onread] (er, br, buf) { this[_reading] = false if (er) this[_onerror](er) else if (this[_handleChunk](br, buf)) this[_read]() } [_close] () { if (this[_autoClose] && typeof this[_fd] === 'number') { const fd = this[_fd] this[_fd] = null fs.close(fd, er => er ? this.emit('error', er) : this.emit('close')) } } [_onerror] (er) { this[_reading] = true this[_close]() this.emit('error', er) } [_handleChunk] (br, buf) { let ret = false // no effect if infinite this[_remain] -= br if (br > 0) ret = super.write(br < buf.length ? buf.slice(0, br) : buf) if (br === 0 || this[_remain] <= 0) { ret = false this[_close]() super.end() } return ret } emit (ev, data) { switch (ev) { case 'prefinish': case 'finish': break case 'drain': if (typeof this[_fd] === 'number') this[_read]() break case 'error': if (this[_errored]) return this[_errored] = true return super.emit(ev, data) default: return super.emit(ev, data) } } } class ReadStreamSync extends ReadStream { [_open] () { let threw = true try { this[_onopen](null, fs.openSync(this[_path], 'r')) threw = false } finally { if (threw) this[_close]() } } [_read] () { let threw = true try { if (!this[_reading]) { this[_reading] = true do { const buf = this[_makeBuf]() /* istanbul ignore next */ const br = buf.length === 0 ? 0 : fs.readSync(this[_fd], buf, 0, buf.length, null) if (!this[_handleChunk](br, buf)) break } while (true) this[_reading] = false } threw = false } finally { if (threw) this[_close]() } } [_close] () { if (this[_autoClose] && typeof this[_fd] === 'number') { const fd = this[_fd] this[_fd] = null fs.closeSync(fd) this.emit('close') } } } class WriteStream extends EE { constructor (path, opt) { opt = opt || {} super(opt) this.readable = false this.writable = true this[_errored] = false this[_writing] = false this[_ended] = false this[_needDrain] = false this[_queue] = [] this[_path] = path this[_fd] = typeof opt.fd === 'number' ? opt.fd : null this[_mode] = opt.mode === undefined ? 0o666 : opt.mode this[_pos] = typeof opt.start === 'number' ? opt.start : null this[_autoClose] = typeof opt.autoClose === 'boolean' ? opt.autoClose : true // truncating makes no sense when writing into the middle const defaultFlag = this[_pos] !== null ? 'r+' : 'w' this[_defaultFlag] = opt.flags === undefined this[_flags] = this[_defaultFlag] ? defaultFlag : opt.flags if (this[_fd] === null) this[_open]() } emit (ev, data) { if (ev === 'error') { if (this[_errored]) return this[_errored] = true } return super.emit(ev, data) } get fd () { return this[_fd] } get path () { return this[_path] } [_onerror] (er) { this[_close]() this[_writing] = true this.emit('error', er) } [_open] () { fs.open(this[_path], this[_flags], this[_mode], (er, fd) => this[_onopen](er, fd)) } [_onopen] (er, fd) { if (this[_defaultFlag] && this[_flags] === 'r+' && er && er.code === 'ENOENT') { this[_flags] = 'w' this[_open]() } else if (er) this[_onerror](er) else { this[_fd] = fd this.emit('open', fd) this[_flush]() } } end (buf, enc) { if (buf) this.write(buf, enc) this[_ended] = true // synthetic after-write logic, where drain/finish live if (!this[_writing] && !this[_queue].length && typeof this[_fd] === 'number') this[_onwrite](null, 0) return this } write (buf, enc) { if (typeof buf === 'string') buf = Buffer.from(buf, enc) if (this[_ended]) { this.emit('error', new Error('write() after end()')) return false } if (this[_fd] === null || this[_writing] || this[_queue].length) { this[_queue].push(buf) this[_needDrain] = true return false } this[_writing] = true this[_write](buf) return true } [_write] (buf) { fs.write(this[_fd], buf, 0, buf.length, this[_pos], (er, bw) => this[_onwrite](er, bw)) } [_onwrite] (er, bw) { if (er) this[_onerror](er) else { if (this[_pos] !== null) this[_pos] += bw if (this[_queue].length) this[_flush]() else { this[_writing] = false if (this[_ended] && !this[_finished]) { this[_finished] = true this[_close]() this.emit('finish') } else if (this[_needDrain]) { this[_needDrain] = false this.emit('drain') } } } } [_flush] () { if (this[_queue].length === 0) { if (this[_ended]) this[_onwrite](null, 0) } else if (this[_queue].length === 1) this[_write](this[_queue].pop()) else { const iovec = this[_queue] this[_queue] = [] writev(this[_fd], iovec, this[_pos], (er, bw) => this[_onwrite](er, bw)) } } [_close] () { if (this[_autoClose] && typeof this[_fd] === 'number') { const fd = this[_fd] this[_fd] = null fs.close(fd, er => er ? this.emit('error', er) : this.emit('close')) } } } class WriteStreamSync extends WriteStream { [_open] () { let fd // only wrap in a try{} block if we know we'll retry, to avoid // the rethrow obscuring the error's source frame in most cases. if (this[_defaultFlag] && this[_flags] === 'r+') { try { fd = fs.openSync(this[_path], this[_flags], this[_mode]) } catch (er) { if (er.code === 'ENOENT') { this[_flags] = 'w' return this[_open]() } else throw er } } else fd = fs.openSync(this[_path], this[_flags], this[_mode]) this[_onopen](null, fd) } [_close] () { if (this[_autoClose] && typeof this[_fd] === 'number') { const fd = this[_fd] this[_fd] = null fs.closeSync(fd) this.emit('close') } } [_write] (buf) { // throw the original, but try to close if it fails let threw = true try { this[_onwrite](null, fs.writeSync(this[_fd], buf, 0, buf.length, this[_pos])) threw = false } finally { if (threw) try { this[_close]() } catch (_) {} } } } exports.ReadStream = ReadStream exports.ReadStreamSync = ReadStreamSync exports.WriteStream = WriteStream exports.WriteStreamSync = WriteStreamSync lib/path-reservations.js000064400000010472151701414140011330 0ustar00// A path exclusive reservation system // reserve([list, of, paths], fn) // When the fn is first in line for all its paths, it // is called with a cb that clears the reservation. // // Used by async unpack to avoid clobbering paths in use, // while still allowing maximal safe parallelization. const assert = require('assert') const normalize = require('./normalize-unicode.js') const stripSlashes = require('./strip-trailing-slashes.js') const { join } = require('path') const platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform const isWindows = platform === 'win32' module.exports = () => { // path => [function or Set] // A Set object means a directory reservation // A fn is a direct reservation on that path const queues = new Map() // fn => {paths:[path,...], dirs:[path, ...]} const reservations = new Map() // return a set of parent dirs for a given path // '/a/b/c/d' -> ['/', '/a', '/a/b', '/a/b/c', '/a/b/c/d'] const getDirs = path => { const dirs = path.split('/').slice(0, -1).reduce((set, path) => { if (set.length) { path = join(set[set.length - 1], path) } set.push(path || '/') return set }, []) return dirs } // functions currently running const running = new Set() // return the queues for each path the function cares about // fn => {paths, dirs} const getQueues = fn => { const res = reservations.get(fn) /* istanbul ignore if - unpossible */ if (!res) { throw new Error('function does not have any path reservations') } return { paths: res.paths.map(path => queues.get(path)), dirs: [...res.dirs].map(path => queues.get(path)), } } // check if fn is first in line for all its paths, and is // included in the first set for all its dir queues const check = fn => { const { paths, dirs } = getQueues(fn) return paths.every(q => q[0] === fn) && dirs.every(q => q[0] instanceof Set && q[0].has(fn)) } // run the function if it's first in line and not already running const run = fn => { if (running.has(fn) || !check(fn)) { return false } running.add(fn) fn(() => clear(fn)) return true } const clear = fn => { if (!running.has(fn)) { return false } const { paths, dirs } = reservations.get(fn) const next = new Set() paths.forEach(path => { const q = queues.get(path) assert.equal(q[0], fn) if (q.length === 1) { queues.delete(path) } else { q.shift() if (typeof q[0] === 'function') { next.add(q[0]) } else { q[0].forEach(fn => next.add(fn)) } } }) dirs.forEach(dir => { const q = queues.get(dir) assert(q[0] instanceof Set) if (q[0].size === 1 && q.length === 1) { queues.delete(dir) } else if (q[0].size === 1) { q.shift() // must be a function or else the Set would've been reused next.add(q[0]) } else { q[0].delete(fn) } }) running.delete(fn) next.forEach(fn => run(fn)) return true } const reserve = (paths, fn) => { // collide on matches across case and unicode normalization // On windows, thanks to the magic of 8.3 shortnames, it is fundamentally // impossible to determine whether two paths refer to the same thing on // disk, without asking the kernel for a shortname. // So, we just pretend that every path matches every other path here, // effectively removing all parallelization on windows. paths = isWindows ? ['win32 parallelization disabled'] : paths.map(p => { // don't need normPath, because we skip this entirely for windows return stripSlashes(join(normalize(p))).toLowerCase() }) const dirs = new Set( paths.map(path => getDirs(path)).reduce((a, b) => a.concat(b)) ) reservations.set(fn, { dirs, paths }) paths.forEach(path => { const q = queues.get(path) if (!q) { queues.set(path, [fn]) } else { q.push(fn) } }) dirs.forEach(dir => { const q = queues.get(dir) if (!q) { queues.set(dir, [new Set([fn])]) } else if (q[q.length - 1] instanceof Set) { q[q.length - 1].add(fn) } else { q.push(new Set([fn])) } }) return run(fn) } return { check, reserve } } lib/unpack.js000064400000062353151701414140007140 0ustar00'use strict' // the PEND/UNPEND stuff tracks whether we're ready to emit end/close yet. // but the path reservations are required to avoid race conditions where // parallelized unpack ops may mess with one another, due to dependencies // (like a Link depending on its target) or destructive operations (like // clobbering an fs object to create one of a different type.) const assert = require('assert') const Parser = require('./parse.js') const fs = require('fs') const fsm = require('fs-minipass') const path = require('path') const mkdir = require('./mkdir.js') const wc = require('./winchars.js') const pathReservations = require('./path-reservations.js') const stripAbsolutePath = require('./strip-absolute-path.js') const normPath = require('./normalize-windows-path.js') const stripSlash = require('./strip-trailing-slashes.js') const normalize = require('./normalize-unicode.js') const ONENTRY = Symbol('onEntry') const CHECKFS = Symbol('checkFs') const CHECKFS2 = Symbol('checkFs2') const PRUNECACHE = Symbol('pruneCache') const ISREUSABLE = Symbol('isReusable') const MAKEFS = Symbol('makeFs') const FILE = Symbol('file') const DIRECTORY = Symbol('directory') const LINK = Symbol('link') const SYMLINK = Symbol('symlink') const HARDLINK = Symbol('hardlink') const UNSUPPORTED = Symbol('unsupported') const CHECKPATH = Symbol('checkPath') const MKDIR = Symbol('mkdir') const ONERROR = Symbol('onError') const PENDING = Symbol('pending') const PEND = Symbol('pend') const UNPEND = Symbol('unpend') const ENDED = Symbol('ended') const MAYBECLOSE = Symbol('maybeClose') const SKIP = Symbol('skip') const DOCHOWN = Symbol('doChown') const UID = Symbol('uid') const GID = Symbol('gid') const CHECKED_CWD = Symbol('checkedCwd') const crypto = require('crypto') const getFlag = require('./get-write-flag.js') const platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform const isWindows = platform === 'win32' const DEFAULT_MAX_DEPTH = 1024 // Unlinks on Windows are not atomic. // // This means that if you have a file entry, followed by another // file entry with an identical name, and you cannot re-use the file // (because it's a hardlink, or because unlink:true is set, or it's // Windows, which does not have useful nlink values), then the unlink // will be committed to the disk AFTER the new file has been written // over the old one, deleting the new file. // // To work around this, on Windows systems, we rename the file and then // delete the renamed file. It's a sloppy kludge, but frankly, I do not // know of a better way to do this, given windows' non-atomic unlink // semantics. // // See: https://github.com/npm/node-tar/issues/183 /* istanbul ignore next */ const unlinkFile = (path, cb) => { if (!isWindows) { return fs.unlink(path, cb) } const name = path + '.DELETE.' + crypto.randomBytes(16).toString('hex') fs.rename(path, name, er => { if (er) { return cb(er) } fs.unlink(name, cb) }) } /* istanbul ignore next */ const unlinkFileSync = path => { if (!isWindows) { return fs.unlinkSync(path) } const name = path + '.DELETE.' + crypto.randomBytes(16).toString('hex') fs.renameSync(path, name) fs.unlinkSync(name) } // this.gid, entry.gid, this.processUid const uint32 = (a, b, c) => a === a >>> 0 ? a : b === b >>> 0 ? b : c // clear the cache if it's a case-insensitive unicode-squashing match. // we can't know if the current file system is case-sensitive or supports // unicode fully, so we check for similarity on the maximally compatible // representation. Err on the side of pruning, since all it's doing is // preventing lstats, and it's not the end of the world if we get a false // positive. // Note that on windows, we always drop the entire cache whenever a // symbolic link is encountered, because 8.3 filenames are impossible // to reason about, and collisions are hazards rather than just failures. const cacheKeyNormalize = path => stripSlash(normPath(normalize(path))) .toLowerCase() const pruneCache = (cache, abs) => { abs = cacheKeyNormalize(abs) for (const path of cache.keys()) { const pnorm = cacheKeyNormalize(path) if (pnorm === abs || pnorm.indexOf(abs + '/') === 0) { cache.delete(path) } } } const dropCache = cache => { for (const key of cache.keys()) { cache.delete(key) } } class Unpack extends Parser { constructor (opt) { if (!opt) { opt = {} } opt.ondone = _ => { this[ENDED] = true this[MAYBECLOSE]() } super(opt) this[CHECKED_CWD] = false this.reservations = pathReservations() this.transform = typeof opt.transform === 'function' ? opt.transform : null this.writable = true this.readable = false this[PENDING] = 0 this[ENDED] = false this.dirCache = opt.dirCache || new Map() if (typeof opt.uid === 'number' || typeof opt.gid === 'number') { // need both or neither if (typeof opt.uid !== 'number' || typeof opt.gid !== 'number') { throw new TypeError('cannot set owner without number uid and gid') } if (opt.preserveOwner) { throw new TypeError( 'cannot preserve owner in archive and also set owner explicitly') } this.uid = opt.uid this.gid = opt.gid this.setOwner = true } else { this.uid = null this.gid = null this.setOwner = false } // default true for root if (opt.preserveOwner === undefined && typeof opt.uid !== 'number') { this.preserveOwner = process.getuid && process.getuid() === 0 } else { this.preserveOwner = !!opt.preserveOwner } this.processUid = (this.preserveOwner || this.setOwner) && process.getuid ? process.getuid() : null this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? process.getgid() : null // prevent excessively deep nesting of subfolders // set to `Infinity` to remove this restriction this.maxDepth = typeof opt.maxDepth === 'number' ? opt.maxDepth : DEFAULT_MAX_DEPTH // mostly just for testing, but useful in some cases. // Forcibly trigger a chown on every entry, no matter what this.forceChown = opt.forceChown === true // turn ><?| in filenames into 0xf000-higher encoded forms this.win32 = !!opt.win32 || isWindows // do not unpack over files that are newer than what's in the archive this.newer = !!opt.newer // do not unpack over ANY files this.keep = !!opt.keep // do not set mtime/atime of extracted entries this.noMtime = !!opt.noMtime // allow .., absolute path entries, and unpacking through symlinks // without this, warn and skip .., relativize absolutes, and error // on symlinks in extraction path this.preservePaths = !!opt.preservePaths // unlink files and links before writing. This breaks existing hard // links, and removes symlink directories rather than erroring this.unlink = !!opt.unlink this.cwd = normPath(path.resolve(opt.cwd || process.cwd())) this.strip = +opt.strip || 0 // if we're not chmodding, then we don't need the process umask this.processUmask = opt.noChmod ? 0 : process.umask() this.umask = typeof opt.umask === 'number' ? opt.umask : this.processUmask // default mode for dirs created as parents this.dmode = opt.dmode || (0o0777 & (~this.umask)) this.fmode = opt.fmode || (0o0666 & (~this.umask)) this.on('entry', entry => this[ONENTRY](entry)) } // a bad or damaged archive is a warning for Parser, but an error // when extracting. Mark those errors as unrecoverable, because // the Unpack contract cannot be met. warn (code, msg, data = {}) { if (code === 'TAR_BAD_ARCHIVE' || code === 'TAR_ABORT') { data.recoverable = false } return super.warn(code, msg, data) } [MAYBECLOSE] () { if (this[ENDED] && this[PENDING] === 0) { this.emit('prefinish') this.emit('finish') this.emit('end') } } [CHECKPATH] (entry) { const p = normPath(entry.path) const parts = p.split('/') if (this.strip) { if (parts.length < this.strip) { return false } if (entry.type === 'Link') { const linkparts = normPath(entry.linkpath).split('/') if (linkparts.length >= this.strip) { entry.linkpath = linkparts.slice(this.strip).join('/') } else { return false } } parts.splice(0, this.strip) entry.path = parts.join('/') } if (isFinite(this.maxDepth) && parts.length > this.maxDepth) { this.warn('TAR_ENTRY_ERROR', 'path excessively deep', { entry, path: p, depth: parts.length, maxDepth: this.maxDepth, }) return false } if (!this.preservePaths) { if (parts.includes('..') || isWindows && /^[a-z]:\.\.$/i.test(parts[0])) { this.warn('TAR_ENTRY_ERROR', `path contains '..'`, { entry, path: p, }) return false } // strip off the root const [root, stripped] = stripAbsolutePath(p) if (root) { entry.path = stripped this.warn('TAR_ENTRY_INFO', `stripping ${root} from absolute path`, { entry, path: p, }) } } if (path.isAbsolute(entry.path)) { entry.absolute = normPath(path.resolve(entry.path)) } else { entry.absolute = normPath(path.resolve(this.cwd, entry.path)) } // if we somehow ended up with a path that escapes the cwd, and we are // not in preservePaths mode, then something is fishy! This should have // been prevented above, so ignore this for coverage. /* istanbul ignore if - defense in depth */ if (!this.preservePaths && entry.absolute.indexOf(this.cwd + '/') !== 0 && entry.absolute !== this.cwd) { this.warn('TAR_ENTRY_ERROR', 'path escaped extraction target', { entry, path: normPath(entry.path), resolvedPath: entry.absolute, cwd: this.cwd, }) return false } // an archive can set properties on the extraction directory, but it // may not replace the cwd with a different kind of thing entirely. if (entry.absolute === this.cwd && entry.type !== 'Directory' && entry.type !== 'GNUDumpDir') { return false } // only encode : chars that aren't drive letter indicators if (this.win32) { const { root: aRoot } = path.win32.parse(entry.absolute) entry.absolute = aRoot + wc.encode(entry.absolute.slice(aRoot.length)) const { root: pRoot } = path.win32.parse(entry.path) entry.path = pRoot + wc.encode(entry.path.slice(pRoot.length)) } return true } [ONENTRY] (entry) { if (!this[CHECKPATH](entry)) { return entry.resume() } assert.equal(typeof entry.absolute, 'string') switch (entry.type) { case 'Directory': case 'GNUDumpDir': if (entry.mode) { entry.mode = entry.mode | 0o700 } // eslint-disable-next-line no-fallthrough case 'File': case 'OldFile': case 'ContiguousFile': case 'Link': case 'SymbolicLink': return this[CHECKFS](entry) case 'CharacterDevice': case 'BlockDevice': case 'FIFO': default: return this[UNSUPPORTED](entry) } } [ONERROR] (er, entry) { // Cwd has to exist, or else nothing works. That's serious. // Other errors are warnings, which raise the error in strict // mode, but otherwise continue on. if (er.name === 'CwdError') { this.emit('error', er) } else { this.warn('TAR_ENTRY_ERROR', er, { entry }) this[UNPEND]() entry.resume() } } [MKDIR] (dir, mode, cb) { mkdir(normPath(dir), { uid: this.uid, gid: this.gid, processUid: this.processUid, processGid: this.processGid, umask: this.processUmask, preserve: this.preservePaths, unlink: this.unlink, cache: this.dirCache, cwd: this.cwd, mode: mode, noChmod: this.noChmod, }, cb) } [DOCHOWN] (entry) { // in preserve owner mode, chown if the entry doesn't match process // in set owner mode, chown if setting doesn't match process return this.forceChown || this.preserveOwner && (typeof entry.uid === 'number' && entry.uid !== this.processUid || typeof entry.gid === 'number' && entry.gid !== this.processGid) || (typeof this.uid === 'number' && this.uid !== this.processUid || typeof this.gid === 'number' && this.gid !== this.processGid) } [UID] (entry) { return uint32(this.uid, entry.uid, this.processUid) } [GID] (entry) { return uint32(this.gid, entry.gid, this.processGid) } [FILE] (entry, fullyDone) { const mode = entry.mode & 0o7777 || this.fmode const stream = new fsm.WriteStream(entry.absolute, { flags: getFlag(entry.size), mode: mode, autoClose: false, }) stream.on('error', er => { if (stream.fd) { fs.close(stream.fd, () => {}) } // flush all the data out so that we aren't left hanging // if the error wasn't actually fatal. otherwise the parse // is blocked, and we never proceed. stream.write = () => true this[ONERROR](er, entry) fullyDone() }) let actions = 1 const done = er => { if (er) { /* istanbul ignore else - we should always have a fd by now */ if (stream.fd) { fs.close(stream.fd, () => {}) } this[ONERROR](er, entry) fullyDone() return } if (--actions === 0) { fs.close(stream.fd, er => { if (er) { this[ONERROR](er, entry) } else { this[UNPEND]() } fullyDone() }) } } stream.on('finish', _ => { // if futimes fails, try utimes // if utimes fails, fail with the original error // same for fchown/chown const abs = entry.absolute const fd = stream.fd if (entry.mtime && !this.noMtime) { actions++ const atime = entry.atime || new Date() const mtime = entry.mtime fs.futimes(fd, atime, mtime, er => er ? fs.utimes(abs, atime, mtime, er2 => done(er2 && er)) : done()) } if (this[DOCHOWN](entry)) { actions++ const uid = this[UID](entry) const gid = this[GID](entry) fs.fchown(fd, uid, gid, er => er ? fs.chown(abs, uid, gid, er2 => done(er2 && er)) : done()) } done() }) const tx = this.transform ? this.transform(entry) || entry : entry if (tx !== entry) { tx.on('error', er => { this[ONERROR](er, entry) fullyDone() }) entry.pipe(tx) } tx.pipe(stream) } [DIRECTORY] (entry, fullyDone) { const mode = entry.mode & 0o7777 || this.dmode this[MKDIR](entry.absolute, mode, er => { if (er) { this[ONERROR](er, entry) fullyDone() return } let actions = 1 const done = _ => { if (--actions === 0) { fullyDone() this[UNPEND]() entry.resume() } } if (entry.mtime && !this.noMtime) { actions++ fs.utimes(entry.absolute, entry.atime || new Date(), entry.mtime, done) } if (this[DOCHOWN](entry)) { actions++ fs.chown(entry.absolute, this[UID](entry), this[GID](entry), done) } done() }) } [UNSUPPORTED] (entry) { entry.unsupported = true this.warn('TAR_ENTRY_UNSUPPORTED', `unsupported entry type: ${entry.type}`, { entry }) entry.resume() } [SYMLINK] (entry, done) { this[LINK](entry, entry.linkpath, 'symlink', done) } [HARDLINK] (entry, done) { const linkpath = normPath(path.resolve(this.cwd, entry.linkpath)) this[LINK](entry, linkpath, 'link', done) } [PEND] () { this[PENDING]++ } [UNPEND] () { this[PENDING]-- this[MAYBECLOSE]() } [SKIP] (entry) { this[UNPEND]() entry.resume() } // Check if we can reuse an existing filesystem entry safely and // overwrite it, rather than unlinking and recreating // Windows doesn't report a useful nlink, so we just never reuse entries [ISREUSABLE] (entry, st) { return entry.type === 'File' && !this.unlink && st.isFile() && st.nlink <= 1 && !isWindows } // check if a thing is there, and if so, try to clobber it [CHECKFS] (entry) { this[PEND]() const paths = [entry.path] if (entry.linkpath) { paths.push(entry.linkpath) } this.reservations.reserve(paths, done => this[CHECKFS2](entry, done)) } [PRUNECACHE] (entry) { // if we are not creating a directory, and the path is in the dirCache, // then that means we are about to delete the directory we created // previously, and it is no longer going to be a directory, and neither // is any of its children. // If a symbolic link is encountered, all bets are off. There is no // reasonable way to sanitize the cache in such a way we will be able to // avoid having filesystem collisions. If this happens with a non-symlink // entry, it'll just fail to unpack, but a symlink to a directory, using an // 8.3 shortname or certain unicode attacks, can evade detection and lead // to arbitrary writes to anywhere on the system. if (entry.type === 'SymbolicLink') { dropCache(this.dirCache) } else if (entry.type !== 'Directory') { pruneCache(this.dirCache, entry.absolute) } } [CHECKFS2] (entry, fullyDone) { this[PRUNECACHE](entry) const done = er => { this[PRUNECACHE](entry) fullyDone(er) } const checkCwd = () => { this[MKDIR](this.cwd, this.dmode, er => { if (er) { this[ONERROR](er, entry) done() return } this[CHECKED_CWD] = true start() }) } const start = () => { if (entry.absolute !== this.cwd) { const parent = normPath(path.dirname(entry.absolute)) if (parent !== this.cwd) { return this[MKDIR](parent, this.dmode, er => { if (er) { this[ONERROR](er, entry) done() return } afterMakeParent() }) } } afterMakeParent() } const afterMakeParent = () => { fs.lstat(entry.absolute, (lstatEr, st) => { if (st && (this.keep || this.newer && st.mtime > entry.mtime)) { this[SKIP](entry) done() return } if (lstatEr || this[ISREUSABLE](entry, st)) { return this[MAKEFS](null, entry, done) } if (st.isDirectory()) { if (entry.type === 'Directory') { const needChmod = !this.noChmod && entry.mode && (st.mode & 0o7777) !== entry.mode const afterChmod = er => this[MAKEFS](er, entry, done) if (!needChmod) { return afterChmod() } return fs.chmod(entry.absolute, entry.mode, afterChmod) } // Not a dir entry, have to remove it. // NB: the only way to end up with an entry that is the cwd // itself, in such a way that == does not detect, is a // tricky windows absolute path with UNC or 8.3 parts (and // preservePaths:true, or else it will have been stripped). // In that case, the user has opted out of path protections // explicitly, so if they blow away the cwd, c'est la vie. if (entry.absolute !== this.cwd) { return fs.rmdir(entry.absolute, er => this[MAKEFS](er, entry, done)) } } // not a dir, and not reusable // don't remove if the cwd, we want that error if (entry.absolute === this.cwd) { return this[MAKEFS](null, entry, done) } unlinkFile(entry.absolute, er => this[MAKEFS](er, entry, done)) }) } if (this[CHECKED_CWD]) { start() } else { checkCwd() } } [MAKEFS] (er, entry, done) { if (er) { this[ONERROR](er, entry) done() return } switch (entry.type) { case 'File': case 'OldFile': case 'ContiguousFile': return this[FILE](entry, done) case 'Link': return this[HARDLINK](entry, done) case 'SymbolicLink': return this[SYMLINK](entry, done) case 'Directory': case 'GNUDumpDir': return this[DIRECTORY](entry, done) } } [LINK] (entry, linkpath, link, done) { // XXX: get the type ('symlink' or 'junction') for windows fs[link](linkpath, entry.absolute, er => { if (er) { this[ONERROR](er, entry) } else { this[UNPEND]() entry.resume() } done() }) } } const callSync = fn => { try { return [null, fn()] } catch (er) { return [er, null] } } class UnpackSync extends Unpack { [MAKEFS] (er, entry) { return super[MAKEFS](er, entry, () => {}) } [CHECKFS] (entry) { this[PRUNECACHE](entry) if (!this[CHECKED_CWD]) { const er = this[MKDIR](this.cwd, this.dmode) if (er) { return this[ONERROR](er, entry) } this[CHECKED_CWD] = true } // don't bother to make the parent if the current entry is the cwd, // we've already checked it. if (entry.absolute !== this.cwd) { const parent = normPath(path.dirname(entry.absolute)) if (parent !== this.cwd) { const mkParent = this[MKDIR](parent, this.dmode) if (mkParent) { return this[ONERROR](mkParent, entry) } } } const [lstatEr, st] = callSync(() => fs.lstatSync(entry.absolute)) if (st && (this.keep || this.newer && st.mtime > entry.mtime)) { return this[SKIP](entry) } if (lstatEr || this[ISREUSABLE](entry, st)) { return this[MAKEFS](null, entry) } if (st.isDirectory()) { if (entry.type === 'Directory') { const needChmod = !this.noChmod && entry.mode && (st.mode & 0o7777) !== entry.mode const [er] = needChmod ? callSync(() => { fs.chmodSync(entry.absolute, entry.mode) }) : [] return this[MAKEFS](er, entry) } // not a dir entry, have to remove it const [er] = callSync(() => fs.rmdirSync(entry.absolute)) this[MAKEFS](er, entry) } // not a dir, and not reusable. // don't remove if it's the cwd, since we want that error. const [er] = entry.absolute === this.cwd ? [] : callSync(() => unlinkFileSync(entry.absolute)) this[MAKEFS](er, entry) } [FILE] (entry, done) { const mode = entry.mode & 0o7777 || this.fmode const oner = er => { let closeError try { fs.closeSync(fd) } catch (e) { closeError = e } if (er || closeError) { this[ONERROR](er || closeError, entry) } done() } let fd try { fd = fs.openSync(entry.absolute, getFlag(entry.size), mode) } catch (er) { return oner(er) } const tx = this.transform ? this.transform(entry) || entry : entry if (tx !== entry) { tx.on('error', er => this[ONERROR](er, entry)) entry.pipe(tx) } tx.on('data', chunk => { try { fs.writeSync(fd, chunk, 0, chunk.length) } catch (er) { oner(er) } }) tx.on('end', _ => { let er = null // try both, falling futimes back to utimes // if either fails, handle the first error if (entry.mtime && !this.noMtime) { const atime = entry.atime || new Date() const mtime = entry.mtime try { fs.futimesSync(fd, atime, mtime) } catch (futimeser) { try { fs.utimesSync(entry.absolute, atime, mtime) } catch (utimeser) { er = futimeser } } } if (this[DOCHOWN](entry)) { const uid = this[UID](entry) const gid = this[GID](entry) try { fs.fchownSync(fd, uid, gid) } catch (fchowner) { try { fs.chownSync(entry.absolute, uid, gid) } catch (chowner) { er = er || fchowner } } } oner(er) }) } [DIRECTORY] (entry, done) { const mode = entry.mode & 0o7777 || this.dmode const er = this[MKDIR](entry.absolute, mode) if (er) { this[ONERROR](er, entry) done() return } if (entry.mtime && !this.noMtime) { try { fs.utimesSync(entry.absolute, entry.atime || new Date(), entry.mtime) } catch (er) {} } if (this[DOCHOWN](entry)) { try { fs.chownSync(entry.absolute, this[UID](entry), this[GID](entry)) } catch (er) {} } done() entry.resume() } [MKDIR] (dir, mode) { try { return mkdir.sync(normPath(dir), { uid: this.uid, gid: this.gid, processUid: this.processUid, processGid: this.processGid, umask: this.processUmask, preserve: this.preservePaths, unlink: this.unlink, cache: this.dirCache, cwd: this.cwd, mode: mode, }) } catch (er) { return er } } [LINK] (entry, linkpath, link, done) { try { fs[link + 'Sync'](linkpath, entry.absolute) done() entry.resume() } catch (er) { return this[ONERROR](er, entry) } } } Unpack.Sync = UnpackSync module.exports = Unpack lib/normalize-unicode.js000064400000000634151701414140011275 0ustar00// warning: extremely hot code path. // This has been meticulously optimized for use // within npm install on large package trees. // Do not edit without careful benchmarking. const normalizeCache = Object.create(null) const { hasOwnProperty } = Object.prototype module.exports = s => { if (!hasOwnProperty.call(normalizeCache, s)) { normalizeCache[s] = s.normalize('NFD') } return normalizeCache[s] } lib/get-write-flag.js000064400000001631151701414140010465 0ustar00// Get the appropriate flag to use for creating files // We use fmap on Windows platforms for files less than // 512kb. This is a fairly low limit, but avoids making // things slower in some cases. Since most of what this // library is used for is extracting tarballs of many // relatively small files in npm packages and the like, // it can be a big boost on Windows platforms. // Only supported in Node v12.9.0 and above. const platform = process.env.__FAKE_PLATFORM__ || process.platform const isWindows = platform === 'win32' const fs = global.__FAKE_TESTING_FS__ || require('fs') /* istanbul ignore next */ const { O_CREAT, O_TRUNC, O_WRONLY, UV_FS_O_FILEMAP = 0 } = fs.constants const fMapEnabled = isWindows && !!UV_FS_O_FILEMAP const fMapLimit = 512 * 1024 const fMapFlag = UV_FS_O_FILEMAP | O_TRUNC | O_CREAT | O_WRONLY module.exports = !fMapEnabled ? () => 'w' : size => size < fMapLimit ? fMapFlag : 'w' lib/list.js000064400000006230151701414140006622 0ustar00'use strict' // XXX: This shares a lot in common with extract.js // maybe some DRY opportunity here? // tar -t const hlo = require('./high-level-opt.js') const Parser = require('./parse.js') const fs = require('fs') const fsm = require('fs-minipass') const path = require('path') const stripSlash = require('./strip-trailing-slashes.js') module.exports = (opt_, files, cb) => { if (typeof opt_ === 'function') { cb = opt_, files = null, opt_ = {} } else if (Array.isArray(opt_)) { files = opt_, opt_ = {} } if (typeof files === 'function') { cb = files, files = null } if (!files) { files = [] } else { files = Array.from(files) } const opt = hlo(opt_) if (opt.sync && typeof cb === 'function') { throw new TypeError('callback not supported for sync tar functions') } if (!opt.file && typeof cb === 'function') { throw new TypeError('callback only supported with file option') } if (files.length) { filesFilter(opt, files) } if (!opt.noResume) { onentryFunction(opt) } return opt.file && opt.sync ? listFileSync(opt) : opt.file ? listFile(opt, cb) : list(opt) } const onentryFunction = opt => { const onentry = opt.onentry opt.onentry = onentry ? e => { onentry(e) e.resume() } : e => e.resume() } // construct a filter that limits the file entries listed // include child entries if a dir is included const filesFilter = (opt, files) => { const map = new Map(files.map(f => [stripSlash(f), true])) const filter = opt.filter const mapHas = (file, r) => { const root = r || path.parse(file).root || '.' const ret = file === root ? false : map.has(file) ? map.get(file) : mapHas(path.dirname(file), root) map.set(file, ret) return ret } opt.filter = filter ? (file, entry) => filter(file, entry) && mapHas(stripSlash(file)) : file => mapHas(stripSlash(file)) } const listFileSync = opt => { const p = list(opt) const file = opt.file let threw = true let fd try { const stat = fs.statSync(file) const readSize = opt.maxReadSize || 16 * 1024 * 1024 if (stat.size < readSize) { p.end(fs.readFileSync(file)) } else { let pos = 0 const buf = Buffer.allocUnsafe(readSize) fd = fs.openSync(file, 'r') while (pos < stat.size) { const bytesRead = fs.readSync(fd, buf, 0, readSize, pos) pos += bytesRead p.write(buf.slice(0, bytesRead)) } p.end() } threw = false } finally { if (threw && fd) { try { fs.closeSync(fd) } catch (er) {} } } } const listFile = (opt, cb) => { const parse = new Parser(opt) const readSize = opt.maxReadSize || 16 * 1024 * 1024 const file = opt.file const p = new Promise((resolve, reject) => { parse.on('error', reject) parse.on('end', resolve) fs.stat(file, (er, stat) => { if (er) { reject(er) } else { const stream = new fsm.ReadStream(file, { readSize: readSize, size: stat.size, }) stream.on('error', reject) stream.pipe(parse) } }) }) return cb ? p.then(cb, cb) : p } const list = opt => new Parser(opt) lib/types.js000064400000002110151701414140007004 0ustar00'use strict' // map types from key to human-friendly name exports.name = new Map([ ['0', 'File'], // same as File ['', 'OldFile'], ['1', 'Link'], ['2', 'SymbolicLink'], // Devices and FIFOs aren't fully supported // they are parsed, but skipped when unpacking ['3', 'CharacterDevice'], ['4', 'BlockDevice'], ['5', 'Directory'], ['6', 'FIFO'], // same as File ['7', 'ContiguousFile'], // pax headers ['g', 'GlobalExtendedHeader'], ['x', 'ExtendedHeader'], // vendor-specific stuff // skip ['A', 'SolarisACL'], // like 5, but with data, which should be skipped ['D', 'GNUDumpDir'], // metadata only, skip ['I', 'Inode'], // data = link path of next file ['K', 'NextFileHasLongLinkpath'], // data = path of next file ['L', 'NextFileHasLongPath'], // skip ['M', 'ContinuationFile'], // like L ['N', 'OldGnuLongPath'], // skip ['S', 'SparseFile'], // skip ['V', 'TapeVolumeHeader'], // like x ['X', 'OldExtendedHeader'], ]) // map the other direction exports.code = new Map(Array.from(exports.name).map(kv => [kv[1], kv[0]])) lib/read-entry.js000064400000005432151701414140007724 0ustar00'use strict' const { Minipass } = require('minipass') const normPath = require('./normalize-windows-path.js') const SLURP = Symbol('slurp') module.exports = class ReadEntry extends Minipass { constructor (header, ex, gex) { super() // read entries always start life paused. this is to avoid the // situation where Minipass's auto-ending empty streams results // in an entry ending before we're ready for it. this.pause() this.extended = ex this.globalExtended = gex this.header = header this.startBlockSize = 512 * Math.ceil(header.size / 512) this.blockRemain = this.startBlockSize this.remain = header.size this.type = header.type this.meta = false this.ignore = false switch (this.type) { case 'File': case 'OldFile': case 'Link': case 'SymbolicLink': case 'CharacterDevice': case 'BlockDevice': case 'Directory': case 'FIFO': case 'ContiguousFile': case 'GNUDumpDir': break case 'NextFileHasLongLinkpath': case 'NextFileHasLongPath': case 'OldGnuLongPath': case 'GlobalExtendedHeader': case 'ExtendedHeader': case 'OldExtendedHeader': this.meta = true break // NOTE: gnutar and bsdtar treat unrecognized types as 'File' // it may be worth doing the same, but with a warning. default: this.ignore = true } this.path = normPath(header.path) this.mode = header.mode if (this.mode) { this.mode = this.mode & 0o7777 } this.uid = header.uid this.gid = header.gid this.uname = header.uname this.gname = header.gname this.size = header.size this.mtime = header.mtime this.atime = header.atime this.ctime = header.ctime this.linkpath = normPath(header.linkpath) this.uname = header.uname this.gname = header.gname if (ex) { this[SLURP](ex) } if (gex) { this[SLURP](gex, true) } } write (data) { const writeLen = data.length if (writeLen > this.blockRemain) { throw new Error('writing more to entry than is appropriate') } const r = this.remain const br = this.blockRemain this.remain = Math.max(0, r - writeLen) this.blockRemain = Math.max(0, br - writeLen) if (this.ignore) { return true } if (r >= writeLen) { return super.write(data) } // r < writeLen return super.write(data.slice(0, r)) } [SLURP] (ex, global) { for (const k in ex) { // we slurp in everything except for the path attribute in // a global extended header, because that's weird. if (ex[k] !== null && ex[k] !== undefined && !(global && k === 'path')) { this[k] = k === 'path' || k === 'linkpath' ? normPath(ex[k]) : ex[k] } } } } lib/winchars.js000064400000001027151701414140007464 0ustar00'use strict' // When writing files on Windows, translate the characters to their // 0xf000 higher-encoded versions. const raw = [ '|', '<', '>', '?', ':', ] const win = raw.map(char => String.fromCharCode(0xf000 + char.charCodeAt(0))) const toWin = new Map(raw.map((char, i) => [char, win[i]])) const toRaw = new Map(win.map((char, i) => [char, raw[i]])) module.exports = { encode: s => raw.reduce((s, c) => s.split(c).join(toWin.get(c)), s), decode: s => win.reduce((s, c) => s.split(c).join(toRaw.get(c)), s), } lib/update.js000064400000001651151701414140007133 0ustar00'use strict' // tar -u const hlo = require('./high-level-opt.js') const r = require('./replace.js') // just call tar.r with the filter and mtimeCache module.exports = (opt_, files, cb) => { const opt = hlo(opt_) if (!opt.file) { throw new TypeError('file is required') } if (opt.gzip || opt.brotli || opt.file.endsWith('.br') || opt.file.endsWith('.tbr')) { throw new TypeError('cannot append to compressed archives') } if (!files || !Array.isArray(files) || !files.length) { throw new TypeError('no files or directories specified') } files = Array.from(files) mtimeFilter(opt) return r(opt, files, cb) } const mtimeFilter = opt => { const filter = opt.filter if (!opt.mtimeCache) { opt.mtimeCache = new Map() } opt.filter = filter ? (path, stat) => filter(path, stat) && !(opt.mtimeCache.get(path) > stat.mtime) : (path, stat) => !(opt.mtimeCache.get(path) > stat.mtime) } lib/header.js000064400000021702151701414140007100 0ustar00'use strict' // parse a 512-byte header block to a data object, or vice-versa // encode returns `true` if a pax extended header is needed, because // the data could not be faithfully encoded in a simple header. // (Also, check header.needPax to see if it needs a pax header.) const types = require('./types.js') const pathModule = require('path').posix const large = require('./large-numbers.js') const SLURP = Symbol('slurp') const TYPE = Symbol('type') class Header { constructor (data, off, ex, gex) { this.cksumValid = false this.needPax = false this.nullBlock = false this.block = null this.path = null this.mode = null this.uid = null this.gid = null this.size = null this.mtime = null this.cksum = null this[TYPE] = '0' this.linkpath = null this.uname = null this.gname = null this.devmaj = 0 this.devmin = 0 this.atime = null this.ctime = null if (Buffer.isBuffer(data)) { this.decode(data, off || 0, ex, gex) } else if (data) { this.set(data) } } decode (buf, off, ex, gex) { if (!off) { off = 0 } if (!buf || !(buf.length >= off + 512)) { throw new Error('need 512 bytes for header') } this.path = decString(buf, off, 100) this.mode = decNumber(buf, off + 100, 8) this.uid = decNumber(buf, off + 108, 8) this.gid = decNumber(buf, off + 116, 8) this.size = decNumber(buf, off + 124, 12) this.mtime = decDate(buf, off + 136, 12) this.cksum = decNumber(buf, off + 148, 12) // if we have extended or global extended headers, apply them now // See https://github.com/npm/node-tar/pull/187 this[SLURP](ex) this[SLURP](gex, true) // old tar versions marked dirs as a file with a trailing / this[TYPE] = decString(buf, off + 156, 1) if (this[TYPE] === '') { this[TYPE] = '0' } if (this[TYPE] === '0' && this.path.slice(-1) === '/') { this[TYPE] = '5' } // tar implementations sometimes incorrectly put the stat(dir).size // as the size in the tarball, even though Directory entries are // not able to have any body at all. In the very rare chance that // it actually DOES have a body, we weren't going to do anything with // it anyway, and it'll just be a warning about an invalid header. if (this[TYPE] === '5') { this.size = 0 } this.linkpath = decString(buf, off + 157, 100) if (buf.slice(off + 257, off + 265).toString() === 'ustar\u000000') { this.uname = decString(buf, off + 265, 32) this.gname = decString(buf, off + 297, 32) this.devmaj = decNumber(buf, off + 329, 8) this.devmin = decNumber(buf, off + 337, 8) if (buf[off + 475] !== 0) { // definitely a prefix, definitely >130 chars. const prefix = decString(buf, off + 345, 155) this.path = prefix + '/' + this.path } else { const prefix = decString(buf, off + 345, 130) if (prefix) { this.path = prefix + '/' + this.path } this.atime = decDate(buf, off + 476, 12) this.ctime = decDate(buf, off + 488, 12) } } let sum = 8 * 0x20 for (let i = off; i < off + 148; i++) { sum += buf[i] } for (let i = off + 156; i < off + 512; i++) { sum += buf[i] } this.cksumValid = sum === this.cksum if (this.cksum === null && sum === 8 * 0x20) { this.nullBlock = true } } [SLURP] (ex, global) { for (const k in ex) { // we slurp in everything except for the path attribute in // a global extended header, because that's weird. if (ex[k] !== null && ex[k] !== undefined && !(global && k === 'path')) { this[k] = ex[k] } } } encode (buf, off) { if (!buf) { buf = this.block = Buffer.alloc(512) off = 0 } if (!off) { off = 0 } if (!(buf.length >= off + 512)) { throw new Error('need 512 bytes for header') } const prefixSize = this.ctime || this.atime ? 130 : 155 const split = splitPrefix(this.path || '', prefixSize) const path = split[0] const prefix = split[1] this.needPax = split[2] this.needPax = encString(buf, off, 100, path) || this.needPax this.needPax = encNumber(buf, off + 100, 8, this.mode) || this.needPax this.needPax = encNumber(buf, off + 108, 8, this.uid) || this.needPax this.needPax = encNumber(buf, off + 116, 8, this.gid) || this.needPax this.needPax = encNumber(buf, off + 124, 12, this.size) || this.needPax this.needPax = encDate(buf, off + 136, 12, this.mtime) || this.needPax buf[off + 156] = this[TYPE].charCodeAt(0) this.needPax = encString(buf, off + 157, 100, this.linkpath) || this.needPax buf.write('ustar\u000000', off + 257, 8) this.needPax = encString(buf, off + 265, 32, this.uname) || this.needPax this.needPax = encString(buf, off + 297, 32, this.gname) || this.needPax this.needPax = encNumber(buf, off + 329, 8, this.devmaj) || this.needPax this.needPax = encNumber(buf, off + 337, 8, this.devmin) || this.needPax this.needPax = encString(buf, off + 345, prefixSize, prefix) || this.needPax if (buf[off + 475] !== 0) { this.needPax = encString(buf, off + 345, 155, prefix) || this.needPax } else { this.needPax = encString(buf, off + 345, 130, prefix) || this.needPax this.needPax = encDate(buf, off + 476, 12, this.atime) || this.needPax this.needPax = encDate(buf, off + 488, 12, this.ctime) || this.needPax } let sum = 8 * 0x20 for (let i = off; i < off + 148; i++) { sum += buf[i] } for (let i = off + 156; i < off + 512; i++) { sum += buf[i] } this.cksum = sum encNumber(buf, off + 148, 8, this.cksum) this.cksumValid = true return this.needPax } set (data) { for (const i in data) { if (data[i] !== null && data[i] !== undefined) { this[i] = data[i] } } } get type () { return types.name.get(this[TYPE]) || this[TYPE] } get typeKey () { return this[TYPE] } set type (type) { if (types.code.has(type)) { this[TYPE] = types.code.get(type) } else { this[TYPE] = type } } } const splitPrefix = (p, prefixSize) => { const pathSize = 100 let pp = p let prefix = '' let ret const root = pathModule.parse(p).root || '.' if (Buffer.byteLength(pp) < pathSize) { ret = [pp, prefix, false] } else { // first set prefix to the dir, and path to the base prefix = pathModule.dirname(pp) pp = pathModule.basename(pp) do { if (Buffer.byteLength(pp) <= pathSize && Buffer.byteLength(prefix) <= prefixSize) { // both fit! ret = [pp, prefix, false] } else if (Buffer.byteLength(pp) > pathSize && Buffer.byteLength(prefix) <= prefixSize) { // prefix fits in prefix, but path doesn't fit in path ret = [pp.slice(0, pathSize - 1), prefix, true] } else { // make path take a bit from prefix pp = pathModule.join(pathModule.basename(prefix), pp) prefix = pathModule.dirname(prefix) } } while (prefix !== root && !ret) // at this point, found no resolution, just truncate if (!ret) { ret = [p.slice(0, pathSize - 1), '', true] } } return ret } const decString = (buf, off, size) => buf.slice(off, off + size).toString('utf8').replace(/\0.*/, '') const decDate = (buf, off, size) => numToDate(decNumber(buf, off, size)) const numToDate = num => num === null ? null : new Date(num * 1000) const decNumber = (buf, off, size) => buf[off] & 0x80 ? large.parse(buf.slice(off, off + size)) : decSmallNumber(buf, off, size) const nanNull = value => isNaN(value) ? null : value const decSmallNumber = (buf, off, size) => nanNull(parseInt( buf.slice(off, off + size) .toString('utf8').replace(/\0.*$/, '').trim(), 8)) // the maximum encodable as a null-terminated octal, by field size const MAXNUM = { 12: 0o77777777777, 8: 0o7777777, } const encNumber = (buf, off, size, number) => number === null ? false : number > MAXNUM[size] || number < 0 ? (large.encode(number, buf.slice(off, off + size)), true) : (encSmallNumber(buf, off, size, number), false) const encSmallNumber = (buf, off, size, number) => buf.write(octalString(number, size), off, size, 'ascii') const octalString = (number, size) => padOctal(Math.floor(number).toString(8), size) const padOctal = (string, size) => (string.length === size - 1 ? string : new Array(size - string.length - 1).join('0') + string + ' ') + '\0' const encDate = (buf, off, size, date) => date === null ? false : encNumber(buf, off, size, date.getTime() / 1000) // enough to fill the longest string we've got const NULLS = new Array(156).join('\0') // pad with nulls, return true if it's longer or non-ascii const encString = (buf, off, size, string) => string === null ? false : (buf.write(string + NULLS, off, size, 'utf8'), string.length !== Buffer.byteLength(string) || string.length > size) module.exports = Header lib/extract.js000064400000005444151701414140007327 0ustar00'use strict' // tar -x const hlo = require('./high-level-opt.js') const Unpack = require('./unpack.js') const fs = require('fs') const fsm = require('fs-minipass') const path = require('path') const stripSlash = require('./strip-trailing-slashes.js') module.exports = (opt_, files, cb) => { if (typeof opt_ === 'function') { cb = opt_, files = null, opt_ = {} } else if (Array.isArray(opt_)) { files = opt_, opt_ = {} } if (typeof files === 'function') { cb = files, files = null } if (!files) { files = [] } else { files = Array.from(files) } const opt = hlo(opt_) if (opt.sync && typeof cb === 'function') { throw new TypeError('callback not supported for sync tar functions') } if (!opt.file && typeof cb === 'function') { throw new TypeError('callback only supported with file option') } if (files.length) { filesFilter(opt, files) } return opt.file && opt.sync ? extractFileSync(opt) : opt.file ? extractFile(opt, cb) : opt.sync ? extractSync(opt) : extract(opt) } // construct a filter that limits the file entries listed // include child entries if a dir is included const filesFilter = (opt, files) => { const map = new Map(files.map(f => [stripSlash(f), true])) const filter = opt.filter const mapHas = (file, r) => { const root = r || path.parse(file).root || '.' const ret = file === root ? false : map.has(file) ? map.get(file) : mapHas(path.dirname(file), root) map.set(file, ret) return ret } opt.filter = filter ? (file, entry) => filter(file, entry) && mapHas(stripSlash(file)) : file => mapHas(stripSlash(file)) } const extractFileSync = opt => { const u = new Unpack.Sync(opt) const file = opt.file const stat = fs.statSync(file) // This trades a zero-byte read() syscall for a stat // However, it will usually result in less memory allocation const readSize = opt.maxReadSize || 16 * 1024 * 1024 const stream = new fsm.ReadStreamSync(file, { readSize: readSize, size: stat.size, }) stream.pipe(u) } const extractFile = (opt, cb) => { const u = new Unpack(opt) const readSize = opt.maxReadSize || 16 * 1024 * 1024 const file = opt.file const p = new Promise((resolve, reject) => { u.on('error', reject) u.on('close', resolve) // This trades a zero-byte read() syscall for a stat // However, it will usually result in less memory allocation fs.stat(file, (er, stat) => { if (er) { reject(er) } else { const stream = new fsm.ReadStream(file, { readSize: readSize, size: stat.size, }) stream.on('error', reject) stream.pipe(u) } }) }) return cb ? p.then(cb, cb) : p } const extractSync = opt => new Unpack.Sync(opt) const extract = opt => new Unpack(opt) lib/parse.js000064400000037702151701414140006771 0ustar00'use strict' // this[BUFFER] is the remainder of a chunk if we're waiting for // the full 512 bytes of a header to come in. We will Buffer.concat() // it to the next write(), which is a mem copy, but a small one. // // this[QUEUE] is a Yallist of entries that haven't been emitted // yet this can only get filled up if the user keeps write()ing after // a write() returns false, or does a write() with more than one entry // // We don't buffer chunks, we always parse them and either create an // entry, or push it into the active entry. The ReadEntry class knows // to throw data away if .ignore=true // // Shift entry off the buffer when it emits 'end', and emit 'entry' for // the next one in the list. // // At any time, we're pushing body chunks into the entry at WRITEENTRY, // and waiting for 'end' on the entry at READENTRY // // ignored entries get .resume() called on them straight away const warner = require('./warn-mixin.js') const Header = require('./header.js') const EE = require('events') const Yallist = require('yallist') const maxMetaEntrySize = 1024 * 1024 const Entry = require('./read-entry.js') const Pax = require('./pax.js') const zlib = require('minizlib') const { nextTick } = require('process') const gzipHeader = Buffer.from([0x1f, 0x8b]) const STATE = Symbol('state') const WRITEENTRY = Symbol('writeEntry') const READENTRY = Symbol('readEntry') const NEXTENTRY = Symbol('nextEntry') const PROCESSENTRY = Symbol('processEntry') const EX = Symbol('extendedHeader') const GEX = Symbol('globalExtendedHeader') const META = Symbol('meta') const EMITMETA = Symbol('emitMeta') const BUFFER = Symbol('buffer') const QUEUE = Symbol('queue') const ENDED = Symbol('ended') const EMITTEDEND = Symbol('emittedEnd') const EMIT = Symbol('emit') const UNZIP = Symbol('unzip') const CONSUMECHUNK = Symbol('consumeChunk') const CONSUMECHUNKSUB = Symbol('consumeChunkSub') const CONSUMEBODY = Symbol('consumeBody') const CONSUMEMETA = Symbol('consumeMeta') const CONSUMEHEADER = Symbol('consumeHeader') const CONSUMING = Symbol('consuming') const BUFFERCONCAT = Symbol('bufferConcat') const MAYBEEND = Symbol('maybeEnd') const WRITING = Symbol('writing') const ABORTED = Symbol('aborted') const DONE = Symbol('onDone') const SAW_VALID_ENTRY = Symbol('sawValidEntry') const SAW_NULL_BLOCK = Symbol('sawNullBlock') const SAW_EOF = Symbol('sawEOF') const CLOSESTREAM = Symbol('closeStream') const noop = _ => true module.exports = warner(class Parser extends EE { constructor (opt) { opt = opt || {} super(opt) this.file = opt.file || '' // set to boolean false when an entry starts. 1024 bytes of \0 // is technically a valid tarball, albeit a boring one. this[SAW_VALID_ENTRY] = null // these BADARCHIVE errors can't be detected early. listen on DONE. this.on(DONE, _ => { if (this[STATE] === 'begin' || this[SAW_VALID_ENTRY] === false) { // either less than 1 block of data, or all entries were invalid. // Either way, probably not even a tarball. this.warn('TAR_BAD_ARCHIVE', 'Unrecognized archive format') } }) if (opt.ondone) { this.on(DONE, opt.ondone) } else { this.on(DONE, _ => { this.emit('prefinish') this.emit('finish') this.emit('end') }) } this.strict = !!opt.strict this.maxMetaEntrySize = opt.maxMetaEntrySize || maxMetaEntrySize this.filter = typeof opt.filter === 'function' ? opt.filter : noop // Unlike gzip, brotli doesn't have any magic bytes to identify it // Users need to explicitly tell us they're extracting a brotli file // Or we infer from the file extension const isTBR = (opt.file && ( opt.file.endsWith('.tar.br') || opt.file.endsWith('.tbr'))) // if it's a tbr file it MIGHT be brotli, but we don't know until // we look at it and verify it's not a valid tar file. this.brotli = !opt.gzip && opt.brotli !== undefined ? opt.brotli : isTBR ? undefined : false // have to set this so that streams are ok piping into it this.writable = true this.readable = false this[QUEUE] = new Yallist() this[BUFFER] = null this[READENTRY] = null this[WRITEENTRY] = null this[STATE] = 'begin' this[META] = '' this[EX] = null this[GEX] = null this[ENDED] = false this[UNZIP] = null this[ABORTED] = false this[SAW_NULL_BLOCK] = false this[SAW_EOF] = false this.on('end', () => this[CLOSESTREAM]()) if (typeof opt.onwarn === 'function') { this.on('warn', opt.onwarn) } if (typeof opt.onentry === 'function') { this.on('entry', opt.onentry) } } [CONSUMEHEADER] (chunk, position) { if (this[SAW_VALID_ENTRY] === null) { this[SAW_VALID_ENTRY] = false } let header try { header = new Header(chunk, position, this[EX], this[GEX]) } catch (er) { return this.warn('TAR_ENTRY_INVALID', er) } if (header.nullBlock) { if (this[SAW_NULL_BLOCK]) { this[SAW_EOF] = true // ending an archive with no entries. pointless, but legal. if (this[STATE] === 'begin') { this[STATE] = 'header' } this[EMIT]('eof') } else { this[SAW_NULL_BLOCK] = true this[EMIT]('nullBlock') } } else { this[SAW_NULL_BLOCK] = false if (!header.cksumValid) { this.warn('TAR_ENTRY_INVALID', 'checksum failure', { header }) } else if (!header.path) { this.warn('TAR_ENTRY_INVALID', 'path is required', { header }) } else { const type = header.type if (/^(Symbolic)?Link$/.test(type) && !header.linkpath) { this.warn('TAR_ENTRY_INVALID', 'linkpath required', { header }) } else if (!/^(Symbolic)?Link$/.test(type) && header.linkpath) { this.warn('TAR_ENTRY_INVALID', 'linkpath forbidden', { header }) } else { const entry = this[WRITEENTRY] = new Entry(header, this[EX], this[GEX]) // we do this for meta & ignored entries as well, because they // are still valid tar, or else we wouldn't know to ignore them if (!this[SAW_VALID_ENTRY]) { if (entry.remain) { // this might be the one! const onend = () => { if (!entry.invalid) { this[SAW_VALID_ENTRY] = true } } entry.on('end', onend) } else { this[SAW_VALID_ENTRY] = true } } if (entry.meta) { if (entry.size > this.maxMetaEntrySize) { entry.ignore = true this[EMIT]('ignoredEntry', entry) this[STATE] = 'ignore' entry.resume() } else if (entry.size > 0) { this[META] = '' entry.on('data', c => this[META] += c) this[STATE] = 'meta' } } else { this[EX] = null entry.ignore = entry.ignore || !this.filter(entry.path, entry) if (entry.ignore) { // probably valid, just not something we care about this[EMIT]('ignoredEntry', entry) this[STATE] = entry.remain ? 'ignore' : 'header' entry.resume() } else { if (entry.remain) { this[STATE] = 'body' } else { this[STATE] = 'header' entry.end() } if (!this[READENTRY]) { this[QUEUE].push(entry) this[NEXTENTRY]() } else { this[QUEUE].push(entry) } } } } } } } [CLOSESTREAM] () { nextTick(() => this.emit('close')) } [PROCESSENTRY] (entry) { let go = true if (!entry) { this[READENTRY] = null go = false } else if (Array.isArray(entry)) { this.emit.apply(this, entry) } else { this[READENTRY] = entry this.emit('entry', entry) if (!entry.emittedEnd) { entry.on('end', _ => this[NEXTENTRY]()) go = false } } return go } [NEXTENTRY] () { do {} while (this[PROCESSENTRY](this[QUEUE].shift())) if (!this[QUEUE].length) { // At this point, there's nothing in the queue, but we may have an // entry which is being consumed (readEntry). // If we don't, then we definitely can handle more data. // If we do, and either it's flowing, or it has never had any data // written to it, then it needs more. // The only other possibility is that it has returned false from a // write() call, so we wait for the next drain to continue. const re = this[READENTRY] const drainNow = !re || re.flowing || re.size === re.remain if (drainNow) { if (!this[WRITING]) { this.emit('drain') } } else { re.once('drain', _ => this.emit('drain')) } } } [CONSUMEBODY] (chunk, position) { // write up to but no more than writeEntry.blockRemain const entry = this[WRITEENTRY] const br = entry.blockRemain const c = (br >= chunk.length && position === 0) ? chunk : chunk.slice(position, position + br) entry.write(c) if (!entry.blockRemain) { this[STATE] = 'header' this[WRITEENTRY] = null entry.end() } return c.length } [CONSUMEMETA] (chunk, position) { const entry = this[WRITEENTRY] const ret = this[CONSUMEBODY](chunk, position) // if we finished, then the entry is reset if (!this[WRITEENTRY]) { this[EMITMETA](entry) } return ret } [EMIT] (ev, data, extra) { if (!this[QUEUE].length && !this[READENTRY]) { this.emit(ev, data, extra) } else { this[QUEUE].push([ev, data, extra]) } } [EMITMETA] (entry) { this[EMIT]('meta', this[META]) switch (entry.type) { case 'ExtendedHeader': case 'OldExtendedHeader': this[EX] = Pax.parse(this[META], this[EX], false) break case 'GlobalExtendedHeader': this[GEX] = Pax.parse(this[META], this[GEX], true) break case 'NextFileHasLongPath': case 'OldGnuLongPath': this[EX] = this[EX] || Object.create(null) this[EX].path = this[META].replace(/\0.*/, '') break case 'NextFileHasLongLinkpath': this[EX] = this[EX] || Object.create(null) this[EX].linkpath = this[META].replace(/\0.*/, '') break /* istanbul ignore next */ default: throw new Error('unknown meta: ' + entry.type) } } abort (error) { this[ABORTED] = true this.emit('abort', error) // always throws, even in non-strict mode this.warn('TAR_ABORT', error, { recoverable: false }) } write (chunk) { if (this[ABORTED]) { return } // first write, might be gzipped const needSniff = this[UNZIP] === null || this.brotli === undefined && this[UNZIP] === false if (needSniff && chunk) { if (this[BUFFER]) { chunk = Buffer.concat([this[BUFFER], chunk]) this[BUFFER] = null } if (chunk.length < gzipHeader.length) { this[BUFFER] = chunk return true } // look for gzip header for (let i = 0; this[UNZIP] === null && i < gzipHeader.length; i++) { if (chunk[i] !== gzipHeader[i]) { this[UNZIP] = false } } const maybeBrotli = this.brotli === undefined if (this[UNZIP] === false && maybeBrotli) { // read the first header to see if it's a valid tar file. If so, // we can safely assume that it's not actually brotli, despite the // .tbr or .tar.br file extension. // if we ended before getting a full chunk, yes, def brotli if (chunk.length < 512) { if (this[ENDED]) { this.brotli = true } else { this[BUFFER] = chunk return true } } else { // if it's tar, it's pretty reliably not brotli, chances of // that happening are astronomical. try { new Header(chunk.slice(0, 512)) this.brotli = false } catch (_) { this.brotli = true } } } if (this[UNZIP] === null || (this[UNZIP] === false && this.brotli)) { const ended = this[ENDED] this[ENDED] = false this[UNZIP] = this[UNZIP] === null ? new zlib.Unzip() : new zlib.BrotliDecompress() this[UNZIP].on('data', chunk => this[CONSUMECHUNK](chunk)) this[UNZIP].on('error', er => this.abort(er)) this[UNZIP].on('end', _ => { this[ENDED] = true this[CONSUMECHUNK]() }) this[WRITING] = true const ret = this[UNZIP][ended ? 'end' : 'write'](chunk) this[WRITING] = false return ret } } this[WRITING] = true if (this[UNZIP]) { this[UNZIP].write(chunk) } else { this[CONSUMECHUNK](chunk) } this[WRITING] = false // return false if there's a queue, or if the current entry isn't flowing const ret = this[QUEUE].length ? false : this[READENTRY] ? this[READENTRY].flowing : true // if we have no queue, then that means a clogged READENTRY if (!ret && !this[QUEUE].length) { this[READENTRY].once('drain', _ => this.emit('drain')) } return ret } [BUFFERCONCAT] (c) { if (c && !this[ABORTED]) { this[BUFFER] = this[BUFFER] ? Buffer.concat([this[BUFFER], c]) : c } } [MAYBEEND] () { if (this[ENDED] && !this[EMITTEDEND] && !this[ABORTED] && !this[CONSUMING]) { this[EMITTEDEND] = true const entry = this[WRITEENTRY] if (entry && entry.blockRemain) { // truncated, likely a damaged file const have = this[BUFFER] ? this[BUFFER].length : 0 this.warn('TAR_BAD_ARCHIVE', `Truncated input (needed ${ entry.blockRemain} more bytes, only ${have} available)`, { entry }) if (this[BUFFER]) { entry.write(this[BUFFER]) } entry.end() } this[EMIT](DONE) } } [CONSUMECHUNK] (chunk) { if (this[CONSUMING]) { this[BUFFERCONCAT](chunk) } else if (!chunk && !this[BUFFER]) { this[MAYBEEND]() } else { this[CONSUMING] = true if (this[BUFFER]) { this[BUFFERCONCAT](chunk) const c = this[BUFFER] this[BUFFER] = null this[CONSUMECHUNKSUB](c) } else { this[CONSUMECHUNKSUB](chunk) } while (this[BUFFER] && this[BUFFER].length >= 512 && !this[ABORTED] && !this[SAW_EOF]) { const c = this[BUFFER] this[BUFFER] = null this[CONSUMECHUNKSUB](c) } this[CONSUMING] = false } if (!this[BUFFER] || this[ENDED]) { this[MAYBEEND]() } } [CONSUMECHUNKSUB] (chunk) { // we know that we are in CONSUMING mode, so anything written goes into // the buffer. Advance the position and put any remainder in the buffer. let position = 0 const length = chunk.length while (position + 512 <= length && !this[ABORTED] && !this[SAW_EOF]) { switch (this[STATE]) { case 'begin': case 'header': this[CONSUMEHEADER](chunk, position) position += 512 break case 'ignore': case 'body': position += this[CONSUMEBODY](chunk, position) break case 'meta': position += this[CONSUMEMETA](chunk, position) break /* istanbul ignore next */ default: throw new Error('invalid state: ' + this[STATE]) } } if (position < length) { if (this[BUFFER]) { this[BUFFER] = Buffer.concat([chunk.slice(position), this[BUFFER]]) } else { this[BUFFER] = chunk.slice(position) } } } end (chunk) { if (!this[ABORTED]) { if (this[UNZIP]) { this[UNZIP].end(chunk) } else { this[ENDED] = true if (this.brotli === undefined) chunk = chunk || Buffer.alloc(0) this.write(chunk) } } } }) lib/strip-trailing-slashes.js000064400000000612151701414140012255 0ustar00// warning: extremely hot code path. // This has been meticulously optimized for use // within npm install on large package trees. // Do not edit without careful benchmarking. module.exports = str => { let i = str.length - 1 let slashesStart = -1 while (i > -1 && str.charAt(i) === '/') { slashesStart = i i-- } return slashesStart === -1 ? str : str.slice(0, slashesStart) } lib/replace.js000064400000013220151701414140007257 0ustar00'use strict' // tar -r const hlo = require('./high-level-opt.js') const Pack = require('./pack.js') const fs = require('fs') const fsm = require('fs-minipass') const t = require('./list.js') const path = require('path') // starting at the head of the file, read a Header // If the checksum is invalid, that's our position to start writing // If it is, jump forward by the specified size (round up to 512) // and try again. // Write the new Pack stream starting there. const Header = require('./header.js') module.exports = (opt_, files, cb) => { const opt = hlo(opt_) if (!opt.file) { throw new TypeError('file is required') } if (opt.gzip || opt.brotli || opt.file.endsWith('.br') || opt.file.endsWith('.tbr')) { throw new TypeError('cannot append to compressed archives') } if (!files || !Array.isArray(files) || !files.length) { throw new TypeError('no files or directories specified') } files = Array.from(files) return opt.sync ? replaceSync(opt, files) : replace(opt, files, cb) } const replaceSync = (opt, files) => { const p = new Pack.Sync(opt) let threw = true let fd let position try { try { fd = fs.openSync(opt.file, 'r+') } catch (er) { if (er.code === 'ENOENT') { fd = fs.openSync(opt.file, 'w+') } else { throw er } } const st = fs.fstatSync(fd) const headBuf = Buffer.alloc(512) POSITION: for (position = 0; position < st.size; position += 512) { for (let bufPos = 0, bytes = 0; bufPos < 512; bufPos += bytes) { bytes = fs.readSync( fd, headBuf, bufPos, headBuf.length - bufPos, position + bufPos ) if (position === 0 && headBuf[0] === 0x1f && headBuf[1] === 0x8b) { throw new Error('cannot append to compressed archives') } if (!bytes) { break POSITION } } const h = new Header(headBuf) if (!h.cksumValid) { break } const entryBlockSize = 512 * Math.ceil(h.size / 512) if (position + entryBlockSize + 512 > st.size) { break } // the 512 for the header we just parsed will be added as well // also jump ahead all the blocks for the body position += entryBlockSize if (opt.mtimeCache) { opt.mtimeCache.set(h.path, h.mtime) } } threw = false streamSync(opt, p, position, fd, files) } finally { if (threw) { try { fs.closeSync(fd) } catch (er) {} } } } const streamSync = (opt, p, position, fd, files) => { const stream = new fsm.WriteStreamSync(opt.file, { fd: fd, start: position, }) p.pipe(stream) addFilesSync(p, files) } const replace = (opt, files, cb) => { files = Array.from(files) const p = new Pack(opt) const getPos = (fd, size, cb_) => { const cb = (er, pos) => { if (er) { fs.close(fd, _ => cb_(er)) } else { cb_(null, pos) } } let position = 0 if (size === 0) { return cb(null, 0) } let bufPos = 0 const headBuf = Buffer.alloc(512) const onread = (er, bytes) => { if (er) { return cb(er) } bufPos += bytes if (bufPos < 512 && bytes) { return fs.read( fd, headBuf, bufPos, headBuf.length - bufPos, position + bufPos, onread ) } if (position === 0 && headBuf[0] === 0x1f && headBuf[1] === 0x8b) { return cb(new Error('cannot append to compressed archives')) } // truncated header if (bufPos < 512) { return cb(null, position) } const h = new Header(headBuf) if (!h.cksumValid) { return cb(null, position) } const entryBlockSize = 512 * Math.ceil(h.size / 512) if (position + entryBlockSize + 512 > size) { return cb(null, position) } position += entryBlockSize + 512 if (position >= size) { return cb(null, position) } if (opt.mtimeCache) { opt.mtimeCache.set(h.path, h.mtime) } bufPos = 0 fs.read(fd, headBuf, 0, 512, position, onread) } fs.read(fd, headBuf, 0, 512, position, onread) } const promise = new Promise((resolve, reject) => { p.on('error', reject) let flag = 'r+' const onopen = (er, fd) => { if (er && er.code === 'ENOENT' && flag === 'r+') { flag = 'w+' return fs.open(opt.file, flag, onopen) } if (er) { return reject(er) } fs.fstat(fd, (er, st) => { if (er) { return fs.close(fd, () => reject(er)) } getPos(fd, st.size, (er, position) => { if (er) { return reject(er) } const stream = new fsm.WriteStream(opt.file, { fd: fd, start: position, }) p.pipe(stream) stream.on('error', reject) stream.on('close', resolve) addFilesAsync(p, files) }) }) } fs.open(opt.file, flag, onopen) }) return cb ? promise.then(cb, cb) : promise } const addFilesSync = (p, files) => { files.forEach(file => { if (file.charAt(0) === '@') { t({ file: path.resolve(p.cwd, file.slice(1)), sync: true, noResume: true, onentry: entry => p.add(entry), }) } else { p.add(file) } }) p.end() } const addFilesAsync = (p, files) => { while (files.length) { const file = files.shift() if (file.charAt(0) === '@') { return t({ file: path.resolve(p.cwd, file.slice(1)), noResume: true, onentry: entry => p.add(entry), }).then(_ => addFilesAsync(p, files)) } else { p.add(file) } } p.end() } lib/high-level-opt.js000064400000001370151701414140010473 0ustar00'use strict' // turn tar(1) style args like `C` into the more verbose things like `cwd` const argmap = new Map([ ['C', 'cwd'], ['f', 'file'], ['z', 'gzip'], ['P', 'preservePaths'], ['U', 'unlink'], ['strip-components', 'strip'], ['stripComponents', 'strip'], ['keep-newer', 'newer'], ['keepNewer', 'newer'], ['keep-newer-files', 'newer'], ['keepNewerFiles', 'newer'], ['k', 'keep'], ['keep-existing', 'keep'], ['keepExisting', 'keep'], ['m', 'noMtime'], ['no-mtime', 'noMtime'], ['p', 'preserveOwner'], ['L', 'follow'], ['h', 'follow'], ]) module.exports = opt => opt ? Object.keys(opt).map(k => [ argmap.has(k) ? argmap.get(k) : k, opt[k], ]).reduce((set, kv) => (set[kv[0]] = kv[1], set), Object.create(null)) : {} lib/normalize-windows-path.js000064400000000632151701414140012271 0ustar00// on windows, either \ or / are valid directory separators. // on unix, \ is a valid character in filenames. // so, on windows, and only on windows, we replace all \ chars with /, // so that we can use / as our one and only directory separator char. const platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform module.exports = platform !== 'win32' ? p => p : p => p && p.replace(/\\/g, '/') lib/pax.js000064400000007744151701414140006452 0ustar00'use strict' const Header = require('./header.js') const path = require('path') class Pax { constructor (obj, global) { this.atime = obj.atime || null this.charset = obj.charset || null this.comment = obj.comment || null this.ctime = obj.ctime || null this.gid = obj.gid || null this.gname = obj.gname || null this.linkpath = obj.linkpath || null this.mtime = obj.mtime || null this.path = obj.path || null this.size = obj.size || null this.uid = obj.uid || null this.uname = obj.uname || null this.dev = obj.dev || null this.ino = obj.ino || null this.nlink = obj.nlink || null this.global = global || false } encode () { const body = this.encodeBody() if (body === '') { return null } const bodyLen = Buffer.byteLength(body) // round up to 512 bytes // add 512 for header const bufLen = 512 * Math.ceil(1 + bodyLen / 512) const buf = Buffer.allocUnsafe(bufLen) // 0-fill the header section, it might not hit every field for (let i = 0; i < 512; i++) { buf[i] = 0 } new Header({ // XXX split the path // then the path should be PaxHeader + basename, but less than 99, // prepend with the dirname path: ('PaxHeader/' + path.basename(this.path)).slice(0, 99), mode: this.mode || 0o644, uid: this.uid || null, gid: this.gid || null, size: bodyLen, mtime: this.mtime || null, type: this.global ? 'GlobalExtendedHeader' : 'ExtendedHeader', linkpath: '', uname: this.uname || '', gname: this.gname || '', devmaj: 0, devmin: 0, atime: this.atime || null, ctime: this.ctime || null, }).encode(buf) buf.write(body, 512, bodyLen, 'utf8') // null pad after the body for (let i = bodyLen + 512; i < buf.length; i++) { buf[i] = 0 } return buf } encodeBody () { return ( this.encodeField('path') + this.encodeField('ctime') + this.encodeField('atime') + this.encodeField('dev') + this.encodeField('ino') + this.encodeField('nlink') + this.encodeField('charset') + this.encodeField('comment') + this.encodeField('gid') + this.encodeField('gname') + this.encodeField('linkpath') + this.encodeField('mtime') + this.encodeField('size') + this.encodeField('uid') + this.encodeField('uname') ) } encodeField (field) { if (this[field] === null || this[field] === undefined) { return '' } const v = this[field] instanceof Date ? this[field].getTime() / 1000 : this[field] const s = ' ' + (field === 'dev' || field === 'ino' || field === 'nlink' ? 'SCHILY.' : '') + field + '=' + v + '\n' const byteLen = Buffer.byteLength(s) // the digits includes the length of the digits in ascii base-10 // so if it's 9 characters, then adding 1 for the 9 makes it 10 // which makes it 11 chars. let digits = Math.floor(Math.log(byteLen) / Math.log(10)) + 1 if (byteLen + digits >= Math.pow(10, digits)) { digits += 1 } const len = digits + byteLen return len + s } } Pax.parse = (string, ex, g) => new Pax(merge(parseKV(string), ex), g) const merge = (a, b) => b ? Object.keys(a).reduce((s, k) => (s[k] = a[k], s), b) : a const parseKV = string => string .replace(/\n$/, '') .split('\n') .reduce(parseKVLine, Object.create(null)) const parseKVLine = (set, line) => { const n = parseInt(line, 10) // XXX Values with \n in them will fail this. // Refactor to not be a naive line-by-line parse. if (n !== Buffer.byteLength(line) + 1) { return set } line = line.slice((n + ' ').length) const kv = line.split('=') const k = kv.shift().replace(/^SCHILY\.(dev|ino|nlink)/, '$1') if (!k) { return set } const v = kv.join('=') set[k] = /^([A-Z]+\.)?([mac]|birth|creation)time$/.test(k) ? new Date(v * 1000) : /^[0-9]+$/.test(v) ? +v : v return set } module.exports = Pax lib/strip-absolute-path.js000064400000001625151701414140011561 0ustar00// unix absolute paths are also absolute on win32, so we use this for both const { isAbsolute, parse } = require('path').win32 // returns [root, stripped] // Note that windows will think that //x/y/z/a has a "root" of //x/y, and in // those cases, we want to sanitize it to x/y/z/a, not z/a, so we strip / // explicitly if it's the first character. // drive-specific relative paths on Windows get their root stripped off even // though they are not absolute, so `c:../foo` becomes ['c:', '../foo'] module.exports = path => { let r = '' let parsed = parse(path) while (isAbsolute(path) || parsed.root) { // windows will think that //x/y/z has a "root" of //x/y/ // but strip the //?/C:/ off of //?/C:/path const root = path.charAt(0) === '/' && path.slice(0, 4) !== '//?/' ? '/' : parsed.root path = path.slice(root.length) r += root parsed = parse(path) } return [r, path] } lib/large-numbers.js000064400000004265151701414140010420 0ustar00'use strict' // Tar can encode large and negative numbers using a leading byte of // 0xff for negative, and 0x80 for positive. const encode = (num, buf) => { if (!Number.isSafeInteger(num)) { // The number is so large that javascript cannot represent it with integer // precision. throw Error('cannot encode number outside of javascript safe integer range') } else if (num < 0) { encodeNegative(num, buf) } else { encodePositive(num, buf) } return buf } const encodePositive = (num, buf) => { buf[0] = 0x80 for (var i = buf.length; i > 1; i--) { buf[i - 1] = num & 0xff num = Math.floor(num / 0x100) } } const encodeNegative = (num, buf) => { buf[0] = 0xff var flipped = false num = num * -1 for (var i = buf.length; i > 1; i--) { var byte = num & 0xff num = Math.floor(num / 0x100) if (flipped) { buf[i - 1] = onesComp(byte) } else if (byte === 0) { buf[i - 1] = 0 } else { flipped = true buf[i - 1] = twosComp(byte) } } } const parse = (buf) => { const pre = buf[0] const value = pre === 0x80 ? pos(buf.slice(1, buf.length)) : pre === 0xff ? twos(buf) : null if (value === null) { throw Error('invalid base256 encoding') } if (!Number.isSafeInteger(value)) { // The number is so large that javascript cannot represent it with integer // precision. throw Error('parsed number outside of javascript safe integer range') } return value } const twos = (buf) => { var len = buf.length var sum = 0 var flipped = false for (var i = len - 1; i > -1; i--) { var byte = buf[i] var f if (flipped) { f = onesComp(byte) } else if (byte === 0) { f = byte } else { flipped = true f = twosComp(byte) } if (f !== 0) { sum -= f * Math.pow(256, len - i - 1) } } return sum } const pos = (buf) => { var len = buf.length var sum = 0 for (var i = len - 1; i > -1; i--) { var byte = buf[i] if (byte !== 0) { sum += byte * Math.pow(256, len - i - 1) } } return sum } const onesComp = byte => (0xff ^ byte) & 0xff const twosComp = byte => ((0xff ^ byte) + 1) & 0xff module.exports = { encode, parse, } lib/write-entry.js000064400000035704151701414140010150 0ustar00'use strict' const { Minipass } = require('minipass') const Pax = require('./pax.js') const Header = require('./header.js') const fs = require('fs') const path = require('path') const normPath = require('./normalize-windows-path.js') const stripSlash = require('./strip-trailing-slashes.js') const prefixPath = (path, prefix) => { if (!prefix) { return normPath(path) } path = normPath(path).replace(/^\.(\/|$)/, '') return stripSlash(prefix) + '/' + path } const maxReadSize = 16 * 1024 * 1024 const PROCESS = Symbol('process') const FILE = Symbol('file') const DIRECTORY = Symbol('directory') const SYMLINK = Symbol('symlink') const HARDLINK = Symbol('hardlink') const HEADER = Symbol('header') const READ = Symbol('read') const LSTAT = Symbol('lstat') const ONLSTAT = Symbol('onlstat') const ONREAD = Symbol('onread') const ONREADLINK = Symbol('onreadlink') const OPENFILE = Symbol('openfile') const ONOPENFILE = Symbol('onopenfile') const CLOSE = Symbol('close') const MODE = Symbol('mode') const AWAITDRAIN = Symbol('awaitDrain') const ONDRAIN = Symbol('ondrain') const PREFIX = Symbol('prefix') const HAD_ERROR = Symbol('hadError') const warner = require('./warn-mixin.js') const winchars = require('./winchars.js') const stripAbsolutePath = require('./strip-absolute-path.js') const modeFix = require('./mode-fix.js') const WriteEntry = warner(class WriteEntry extends Minipass { constructor (p, opt) { opt = opt || {} super(opt) if (typeof p !== 'string') { throw new TypeError('path is required') } this.path = normPath(p) // suppress atime, ctime, uid, gid, uname, gname this.portable = !!opt.portable // until node has builtin pwnam functions, this'll have to do this.myuid = process.getuid && process.getuid() || 0 this.myuser = process.env.USER || '' this.maxReadSize = opt.maxReadSize || maxReadSize this.linkCache = opt.linkCache || new Map() this.statCache = opt.statCache || new Map() this.preservePaths = !!opt.preservePaths this.cwd = normPath(opt.cwd || process.cwd()) this.strict = !!opt.strict this.noPax = !!opt.noPax this.noMtime = !!opt.noMtime this.mtime = opt.mtime || null this.prefix = opt.prefix ? normPath(opt.prefix) : null this.fd = null this.blockLen = null this.blockRemain = null this.buf = null this.offset = null this.length = null this.pos = null this.remain = null if (typeof opt.onwarn === 'function') { this.on('warn', opt.onwarn) } let pathWarn = false if (!this.preservePaths) { const [root, stripped] = stripAbsolutePath(this.path) if (root) { this.path = stripped pathWarn = root } } this.win32 = !!opt.win32 || process.platform === 'win32' if (this.win32) { // force the \ to / normalization, since we might not *actually* // be on windows, but want \ to be considered a path separator. this.path = winchars.decode(this.path.replace(/\\/g, '/')) p = p.replace(/\\/g, '/') } this.absolute = normPath(opt.absolute || path.resolve(this.cwd, p)) if (this.path === '') { this.path = './' } if (pathWarn) { this.warn('TAR_ENTRY_INFO', `stripping ${pathWarn} from absolute path`, { entry: this, path: pathWarn + this.path, }) } if (this.statCache.has(this.absolute)) { this[ONLSTAT](this.statCache.get(this.absolute)) } else { this[LSTAT]() } } emit (ev, ...data) { if (ev === 'error') { this[HAD_ERROR] = true } return super.emit(ev, ...data) } [LSTAT] () { fs.lstat(this.absolute, (er, stat) => { if (er) { return this.emit('error', er) } this[ONLSTAT](stat) }) } [ONLSTAT] (stat) { this.statCache.set(this.absolute, stat) this.stat = stat if (!stat.isFile()) { stat.size = 0 } this.type = getType(stat) this.emit('stat', stat) this[PROCESS]() } [PROCESS] () { switch (this.type) { case 'File': return this[FILE]() case 'Directory': return this[DIRECTORY]() case 'SymbolicLink': return this[SYMLINK]() // unsupported types are ignored. default: return this.end() } } [MODE] (mode) { return modeFix(mode, this.type === 'Directory', this.portable) } [PREFIX] (path) { return prefixPath(path, this.prefix) } [HEADER] () { if (this.type === 'Directory' && this.portable) { this.noMtime = true } this.header = new Header({ path: this[PREFIX](this.path), // only apply the prefix to hard links. linkpath: this.type === 'Link' ? this[PREFIX](this.linkpath) : this.linkpath, // only the permissions and setuid/setgid/sticky bitflags // not the higher-order bits that specify file type mode: this[MODE](this.stat.mode), uid: this.portable ? null : this.stat.uid, gid: this.portable ? null : this.stat.gid, size: this.stat.size, mtime: this.noMtime ? null : this.mtime || this.stat.mtime, type: this.type, uname: this.portable ? null : this.stat.uid === this.myuid ? this.myuser : '', atime: this.portable ? null : this.stat.atime, ctime: this.portable ? null : this.stat.ctime, }) if (this.header.encode() && !this.noPax) { super.write(new Pax({ atime: this.portable ? null : this.header.atime, ctime: this.portable ? null : this.header.ctime, gid: this.portable ? null : this.header.gid, mtime: this.noMtime ? null : this.mtime || this.header.mtime, path: this[PREFIX](this.path), linkpath: this.type === 'Link' ? this[PREFIX](this.linkpath) : this.linkpath, size: this.header.size, uid: this.portable ? null : this.header.uid, uname: this.portable ? null : this.header.uname, dev: this.portable ? null : this.stat.dev, ino: this.portable ? null : this.stat.ino, nlink: this.portable ? null : this.stat.nlink, }).encode()) } super.write(this.header.block) } [DIRECTORY] () { if (this.path.slice(-1) !== '/') { this.path += '/' } this.stat.size = 0 this[HEADER]() this.end() } [SYMLINK] () { fs.readlink(this.absolute, (er, linkpath) => { if (er) { return this.emit('error', er) } this[ONREADLINK](linkpath) }) } [ONREADLINK] (linkpath) { this.linkpath = normPath(linkpath) this[HEADER]() this.end() } [HARDLINK] (linkpath) { this.type = 'Link' this.linkpath = normPath(path.relative(this.cwd, linkpath)) this.stat.size = 0 this[HEADER]() this.end() } [FILE] () { if (this.stat.nlink > 1) { const linkKey = this.stat.dev + ':' + this.stat.ino if (this.linkCache.has(linkKey)) { const linkpath = this.linkCache.get(linkKey) if (linkpath.indexOf(this.cwd) === 0) { return this[HARDLINK](linkpath) } } this.linkCache.set(linkKey, this.absolute) } this[HEADER]() if (this.stat.size === 0) { return this.end() } this[OPENFILE]() } [OPENFILE] () { fs.open(this.absolute, 'r', (er, fd) => { if (er) { return this.emit('error', er) } this[ONOPENFILE](fd) }) } [ONOPENFILE] (fd) { this.fd = fd if (this[HAD_ERROR]) { return this[CLOSE]() } this.blockLen = 512 * Math.ceil(this.stat.size / 512) this.blockRemain = this.blockLen const bufLen = Math.min(this.blockLen, this.maxReadSize) this.buf = Buffer.allocUnsafe(bufLen) this.offset = 0 this.pos = 0 this.remain = this.stat.size this.length = this.buf.length this[READ]() } [READ] () { const { fd, buf, offset, length, pos } = this fs.read(fd, buf, offset, length, pos, (er, bytesRead) => { if (er) { // ignoring the error from close(2) is a bad practice, but at // this point we already have an error, don't need another one return this[CLOSE](() => this.emit('error', er)) } this[ONREAD](bytesRead) }) } [CLOSE] (cb) { fs.close(this.fd, cb) } [ONREAD] (bytesRead) { if (bytesRead <= 0 && this.remain > 0) { const er = new Error('encountered unexpected EOF') er.path = this.absolute er.syscall = 'read' er.code = 'EOF' return this[CLOSE](() => this.emit('error', er)) } if (bytesRead > this.remain) { const er = new Error('did not encounter expected EOF') er.path = this.absolute er.syscall = 'read' er.code = 'EOF' return this[CLOSE](() => this.emit('error', er)) } // null out the rest of the buffer, if we could fit the block padding // at the end of this loop, we've incremented bytesRead and this.remain // to be incremented up to the blockRemain level, as if we had expected // to get a null-padded file, and read it until the end. then we will // decrement both remain and blockRemain by bytesRead, and know that we // reached the expected EOF, without any null buffer to append. if (bytesRead === this.remain) { for (let i = bytesRead; i < this.length && bytesRead < this.blockRemain; i++) { this.buf[i + this.offset] = 0 bytesRead++ this.remain++ } } const writeBuf = this.offset === 0 && bytesRead === this.buf.length ? this.buf : this.buf.slice(this.offset, this.offset + bytesRead) const flushed = this.write(writeBuf) if (!flushed) { this[AWAITDRAIN](() => this[ONDRAIN]()) } else { this[ONDRAIN]() } } [AWAITDRAIN] (cb) { this.once('drain', cb) } write (writeBuf) { if (this.blockRemain < writeBuf.length) { const er = new Error('writing more data than expected') er.path = this.absolute return this.emit('error', er) } this.remain -= writeBuf.length this.blockRemain -= writeBuf.length this.pos += writeBuf.length this.offset += writeBuf.length return super.write(writeBuf) } [ONDRAIN] () { if (!this.remain) { if (this.blockRemain) { super.write(Buffer.alloc(this.blockRemain)) } return this[CLOSE](er => er ? this.emit('error', er) : this.end()) } if (this.offset >= this.length) { // if we only have a smaller bit left to read, alloc a smaller buffer // otherwise, keep it the same length it was before. this.buf = Buffer.allocUnsafe(Math.min(this.blockRemain, this.buf.length)) this.offset = 0 } this.length = this.buf.length - this.offset this[READ]() } }) class WriteEntrySync extends WriteEntry { [LSTAT] () { this[ONLSTAT](fs.lstatSync(this.absolute)) } [SYMLINK] () { this[ONREADLINK](fs.readlinkSync(this.absolute)) } [OPENFILE] () { this[ONOPENFILE](fs.openSync(this.absolute, 'r')) } [READ] () { let threw = true try { const { fd, buf, offset, length, pos } = this const bytesRead = fs.readSync(fd, buf, offset, length, pos) this[ONREAD](bytesRead) threw = false } finally { // ignoring the error from close(2) is a bad practice, but at // this point we already have an error, don't need another one if (threw) { try { this[CLOSE](() => {}) } catch (er) {} } } } [AWAITDRAIN] (cb) { cb() } [CLOSE] (cb) { fs.closeSync(this.fd) cb() } } const WriteEntryTar = warner(class WriteEntryTar extends Minipass { constructor (readEntry, opt) { opt = opt || {} super(opt) this.preservePaths = !!opt.preservePaths this.portable = !!opt.portable this.strict = !!opt.strict this.noPax = !!opt.noPax this.noMtime = !!opt.noMtime this.readEntry = readEntry this.type = readEntry.type if (this.type === 'Directory' && this.portable) { this.noMtime = true } this.prefix = opt.prefix || null this.path = normPath(readEntry.path) this.mode = this[MODE](readEntry.mode) this.uid = this.portable ? null : readEntry.uid this.gid = this.portable ? null : readEntry.gid this.uname = this.portable ? null : readEntry.uname this.gname = this.portable ? null : readEntry.gname this.size = readEntry.size this.mtime = this.noMtime ? null : opt.mtime || readEntry.mtime this.atime = this.portable ? null : readEntry.atime this.ctime = this.portable ? null : readEntry.ctime this.linkpath = normPath(readEntry.linkpath) if (typeof opt.onwarn === 'function') { this.on('warn', opt.onwarn) } let pathWarn = false if (!this.preservePaths) { const [root, stripped] = stripAbsolutePath(this.path) if (root) { this.path = stripped pathWarn = root } } this.remain = readEntry.size this.blockRemain = readEntry.startBlockSize this.header = new Header({ path: this[PREFIX](this.path), linkpath: this.type === 'Link' ? this[PREFIX](this.linkpath) : this.linkpath, // only the permissions and setuid/setgid/sticky bitflags // not the higher-order bits that specify file type mode: this.mode, uid: this.portable ? null : this.uid, gid: this.portable ? null : this.gid, size: this.size, mtime: this.noMtime ? null : this.mtime, type: this.type, uname: this.portable ? null : this.uname, atime: this.portable ? null : this.atime, ctime: this.portable ? null : this.ctime, }) if (pathWarn) { this.warn('TAR_ENTRY_INFO', `stripping ${pathWarn} from absolute path`, { entry: this, path: pathWarn + this.path, }) } if (this.header.encode() && !this.noPax) { super.write(new Pax({ atime: this.portable ? null : this.atime, ctime: this.portable ? null : this.ctime, gid: this.portable ? null : this.gid, mtime: this.noMtime ? null : this.mtime, path: this[PREFIX](this.path), linkpath: this.type === 'Link' ? this[PREFIX](this.linkpath) : this.linkpath, size: this.size, uid: this.portable ? null : this.uid, uname: this.portable ? null : this.uname, dev: this.portable ? null : this.readEntry.dev, ino: this.portable ? null : this.readEntry.ino, nlink: this.portable ? null : this.readEntry.nlink, }).encode()) } super.write(this.header.block) readEntry.pipe(this) } [PREFIX] (path) { return prefixPath(path, this.prefix) } [MODE] (mode) { return modeFix(mode, this.type === 'Directory', this.portable) } write (data) { const writeLen = data.length if (writeLen > this.blockRemain) { throw new Error('writing more to entry than is appropriate') } this.blockRemain -= writeLen return super.write(data) } end () { if (this.blockRemain) { super.write(Buffer.alloc(this.blockRemain)) } return super.end() } }) WriteEntry.Sync = WriteEntrySync WriteEntry.Tar = WriteEntryTar const getType = stat => stat.isFile() ? 'File' : stat.isDirectory() ? 'Directory' : stat.isSymbolicLink() ? 'SymbolicLink' : 'Unsupported' module.exports = WriteEntry lib/warn-mixin.js000064400000001325151701414140007740 0ustar00'use strict' module.exports = Base => class extends Base { warn (code, message, data = {}) { if (this.file) { data.file = this.file } if (this.cwd) { data.cwd = this.cwd } data.code = message instanceof Error && message.code || code data.tarCode = code if (!this.strict && data.recoverable !== false) { if (message instanceof Error) { data = Object.assign(message, data) message = message.message } this.emit('warn', data.tarCode, message, data) } else if (message instanceof Error) { this.emit('error', Object.assign(message, data)) } else { this.emit('error', Object.assign(new Error(`${code}: ${message}`), data)) } } } lib/pack.js000064400000023445151701414140006574 0ustar00'use strict' // A readable tar stream creator // Technically, this is a transform stream that you write paths into, // and tar format comes out of. // The `add()` method is like `write()` but returns this, // and end() return `this` as well, so you can // do `new Pack(opt).add('files').add('dir').end().pipe(output) // You could also do something like: // streamOfPaths().pipe(new Pack()).pipe(new fs.WriteStream('out.tar')) class PackJob { constructor (path, absolute) { this.path = path || './' this.absolute = absolute this.entry = null this.stat = null this.readdir = null this.pending = false this.ignore = false this.piped = false } } const { Minipass } = require('minipass') const zlib = require('minizlib') const ReadEntry = require('./read-entry.js') const WriteEntry = require('./write-entry.js') const WriteEntrySync = WriteEntry.Sync const WriteEntryTar = WriteEntry.Tar const Yallist = require('yallist') const EOF = Buffer.alloc(1024) const ONSTAT = Symbol('onStat') const ENDED = Symbol('ended') const QUEUE = Symbol('queue') const CURRENT = Symbol('current') const PROCESS = Symbol('process') const PROCESSING = Symbol('processing') const PROCESSJOB = Symbol('processJob') const JOBS = Symbol('jobs') const JOBDONE = Symbol('jobDone') const ADDFSENTRY = Symbol('addFSEntry') const ADDTARENTRY = Symbol('addTarEntry') const STAT = Symbol('stat') const READDIR = Symbol('readdir') const ONREADDIR = Symbol('onreaddir') const PIPE = Symbol('pipe') const ENTRY = Symbol('entry') const ENTRYOPT = Symbol('entryOpt') const WRITEENTRYCLASS = Symbol('writeEntryClass') const WRITE = Symbol('write') const ONDRAIN = Symbol('ondrain') const fs = require('fs') const path = require('path') const warner = require('./warn-mixin.js') const normPath = require('./normalize-windows-path.js') const Pack = warner(class Pack extends Minipass { constructor (opt) { super(opt) opt = opt || Object.create(null) this.opt = opt this.file = opt.file || '' this.cwd = opt.cwd || process.cwd() this.maxReadSize = opt.maxReadSize this.preservePaths = !!opt.preservePaths this.strict = !!opt.strict this.noPax = !!opt.noPax this.prefix = normPath(opt.prefix || '') this.linkCache = opt.linkCache || new Map() this.statCache = opt.statCache || new Map() this.readdirCache = opt.readdirCache || new Map() this[WRITEENTRYCLASS] = WriteEntry if (typeof opt.onwarn === 'function') { this.on('warn', opt.onwarn) } this.portable = !!opt.portable this.zip = null if (opt.gzip || opt.brotli) { if (opt.gzip && opt.brotli) { throw new TypeError('gzip and brotli are mutually exclusive') } if (opt.gzip) { if (typeof opt.gzip !== 'object') { opt.gzip = {} } if (this.portable) { opt.gzip.portable = true } this.zip = new zlib.Gzip(opt.gzip) } if (opt.brotli) { if (typeof opt.brotli !== 'object') { opt.brotli = {} } this.zip = new zlib.BrotliCompress(opt.brotli) } this.zip.on('data', chunk => super.write(chunk)) this.zip.on('end', _ => super.end()) this.zip.on('drain', _ => this[ONDRAIN]()) this.on('resume', _ => this.zip.resume()) } else { this.on('drain', this[ONDRAIN]) } this.noDirRecurse = !!opt.noDirRecurse this.follow = !!opt.follow this.noMtime = !!opt.noMtime this.mtime = opt.mtime || null this.filter = typeof opt.filter === 'function' ? opt.filter : _ => true this[QUEUE] = new Yallist() this[JOBS] = 0 this.jobs = +opt.jobs || 4 this[PROCESSING] = false this[ENDED] = false } [WRITE] (chunk) { return super.write(chunk) } add (path) { this.write(path) return this } end (path) { if (path) { this.write(path) } this[ENDED] = true this[PROCESS]() return this } write (path) { if (this[ENDED]) { throw new Error('write after end') } if (path instanceof ReadEntry) { this[ADDTARENTRY](path) } else { this[ADDFSENTRY](path) } return this.flowing } [ADDTARENTRY] (p) { const absolute = normPath(path.resolve(this.cwd, p.path)) // in this case, we don't have to wait for the stat if (!this.filter(p.path, p)) { p.resume() } else { const job = new PackJob(p.path, absolute, false) job.entry = new WriteEntryTar(p, this[ENTRYOPT](job)) job.entry.on('end', _ => this[JOBDONE](job)) this[JOBS] += 1 this[QUEUE].push(job) } this[PROCESS]() } [ADDFSENTRY] (p) { const absolute = normPath(path.resolve(this.cwd, p)) this[QUEUE].push(new PackJob(p, absolute)) this[PROCESS]() } [STAT] (job) { job.pending = true this[JOBS] += 1 const stat = this.follow ? 'stat' : 'lstat' fs[stat](job.absolute, (er, stat) => { job.pending = false this[JOBS] -= 1 if (er) { this.emit('error', er) } else { this[ONSTAT](job, stat) } }) } [ONSTAT] (job, stat) { this.statCache.set(job.absolute, stat) job.stat = stat // now we have the stat, we can filter it. if (!this.filter(job.path, stat)) { job.ignore = true } this[PROCESS]() } [READDIR] (job) { job.pending = true this[JOBS] += 1 fs.readdir(job.absolute, (er, entries) => { job.pending = false this[JOBS] -= 1 if (er) { return this.emit('error', er) } this[ONREADDIR](job, entries) }) } [ONREADDIR] (job, entries) { this.readdirCache.set(job.absolute, entries) job.readdir = entries this[PROCESS]() } [PROCESS] () { if (this[PROCESSING]) { return } this[PROCESSING] = true for (let w = this[QUEUE].head; w !== null && this[JOBS] < this.jobs; w = w.next) { this[PROCESSJOB](w.value) if (w.value.ignore) { const p = w.next this[QUEUE].removeNode(w) w.next = p } } this[PROCESSING] = false if (this[ENDED] && !this[QUEUE].length && this[JOBS] === 0) { if (this.zip) { this.zip.end(EOF) } else { super.write(EOF) super.end() } } } get [CURRENT] () { return this[QUEUE] && this[QUEUE].head && this[QUEUE].head.value } [JOBDONE] (job) { this[QUEUE].shift() this[JOBS] -= 1 this[PROCESS]() } [PROCESSJOB] (job) { if (job.pending) { return } if (job.entry) { if (job === this[CURRENT] && !job.piped) { this[PIPE](job) } return } if (!job.stat) { if (this.statCache.has(job.absolute)) { this[ONSTAT](job, this.statCache.get(job.absolute)) } else { this[STAT](job) } } if (!job.stat) { return } // filtered out! if (job.ignore) { return } if (!this.noDirRecurse && job.stat.isDirectory() && !job.readdir) { if (this.readdirCache.has(job.absolute)) { this[ONREADDIR](job, this.readdirCache.get(job.absolute)) } else { this[READDIR](job) } if (!job.readdir) { return } } // we know it doesn't have an entry, because that got checked above job.entry = this[ENTRY](job) if (!job.entry) { job.ignore = true return } if (job === this[CURRENT] && !job.piped) { this[PIPE](job) } } [ENTRYOPT] (job) { return { onwarn: (code, msg, data) => this.warn(code, msg, data), noPax: this.noPax, cwd: this.cwd, absolute: job.absolute, preservePaths: this.preservePaths, maxReadSize: this.maxReadSize, strict: this.strict, portable: this.portable, linkCache: this.linkCache, statCache: this.statCache, noMtime: this.noMtime, mtime: this.mtime, prefix: this.prefix, } } [ENTRY] (job) { this[JOBS] += 1 try { return new this[WRITEENTRYCLASS](job.path, this[ENTRYOPT](job)) .on('end', () => this[JOBDONE](job)) .on('error', er => this.emit('error', er)) } catch (er) { this.emit('error', er) } } [ONDRAIN] () { if (this[CURRENT] && this[CURRENT].entry) { this[CURRENT].entry.resume() } } // like .pipe() but using super, because our write() is special [PIPE] (job) { job.piped = true if (job.readdir) { job.readdir.forEach(entry => { const p = job.path const base = p === './' ? '' : p.replace(/\/*$/, '/') this[ADDFSENTRY](base + entry) }) } const source = job.entry const zip = this.zip if (zip) { source.on('data', chunk => { if (!zip.write(chunk)) { source.pause() } }) } else { source.on('data', chunk => { if (!super.write(chunk)) { source.pause() } }) } } pause () { if (this.zip) { this.zip.pause() } return super.pause() } }) class PackSync extends Pack { constructor (opt) { super(opt) this[WRITEENTRYCLASS] = WriteEntrySync } // pause/resume are no-ops in sync streams. pause () {} resume () {} [STAT] (job) { const stat = this.follow ? 'statSync' : 'lstatSync' this[ONSTAT](job, fs[stat](job.absolute)) } [READDIR] (job, stat) { this[ONREADDIR](job, fs.readdirSync(job.absolute)) } // gotta get it all in this tick [PIPE] (job) { const source = job.entry const zip = this.zip if (job.readdir) { job.readdir.forEach(entry => { const p = job.path const base = p === './' ? '' : p.replace(/\/*$/, '/') this[ADDFSENTRY](base + entry) }) } if (zip) { source.on('data', chunk => { zip.write(chunk) }) } else { source.on('data', chunk => { super[WRITE](chunk) }) } } } Pack.Sync = PackSync module.exports = Pack lib/create.js000064400000004533151701414140007116 0ustar00'use strict' // tar -c const hlo = require('./high-level-opt.js') const Pack = require('./pack.js') const fsm = require('fs-minipass') const t = require('./list.js') const path = require('path') module.exports = (opt_, files, cb) => { if (typeof files === 'function') { cb = files } if (Array.isArray(opt_)) { files = opt_, opt_ = {} } if (!files || !Array.isArray(files) || !files.length) { throw new TypeError('no files or directories specified') } files = Array.from(files) const opt = hlo(opt_) if (opt.sync && typeof cb === 'function') { throw new TypeError('callback not supported for sync tar functions') } if (!opt.file && typeof cb === 'function') { throw new TypeError('callback only supported with file option') } return opt.file && opt.sync ? createFileSync(opt, files) : opt.file ? createFile(opt, files, cb) : opt.sync ? createSync(opt, files) : create(opt, files) } const createFileSync = (opt, files) => { const p = new Pack.Sync(opt) const stream = new fsm.WriteStreamSync(opt.file, { mode: opt.mode || 0o666, }) p.pipe(stream) addFilesSync(p, files) } const createFile = (opt, files, cb) => { const p = new Pack(opt) const stream = new fsm.WriteStream(opt.file, { mode: opt.mode || 0o666, }) p.pipe(stream) const promise = new Promise((res, rej) => { stream.on('error', rej) stream.on('close', res) p.on('error', rej) }) addFilesAsync(p, files) return cb ? promise.then(cb, cb) : promise } const addFilesSync = (p, files) => { files.forEach(file => { if (file.charAt(0) === '@') { t({ file: path.resolve(p.cwd, file.slice(1)), sync: true, noResume: true, onentry: entry => p.add(entry), }) } else { p.add(file) } }) p.end() } const addFilesAsync = (p, files) => { while (files.length) { const file = files.shift() if (file.charAt(0) === '@') { return t({ file: path.resolve(p.cwd, file.slice(1)), noResume: true, onentry: entry => p.add(entry), }).then(_ => addFilesAsync(p, files)) } else { p.add(file) } } p.end() } const createSync = (opt, files) => { const p = new Pack.Sync(opt) addFilesSync(p, files) return p } const create = (opt, files) => { const p = new Pack(opt) addFilesAsync(p, files) return p } lib/mode-fix.js000064400000001211151701414140007351 0ustar00'use strict' module.exports = (mode, isDir, portable) => { mode &= 0o7777 // in portable mode, use the minimum reasonable umask // if this system creates files with 0o664 by default // (as some linux distros do), then we'll write the // archive with 0o644 instead. Also, don't ever create // a file that is not readable/writable by the owner. if (portable) { mode = (mode | 0o600) & ~0o22 } // if dirs are readable, then they should be listable if (isDir) { if (mode & 0o400) { mode |= 0o100 } if (mode & 0o40) { mode |= 0o10 } if (mode & 0o4) { mode |= 0o1 } } return mode } lib/mkdir.js000064400000012555151701414140006764 0ustar00'use strict' // wrapper around mkdirp for tar's needs. // TODO: This should probably be a class, not functionally // passing around state in a gazillion args. const mkdirp = require('mkdirp') const fs = require('fs') const path = require('path') const chownr = require('chownr') const normPath = require('./normalize-windows-path.js') class SymlinkError extends Error { constructor (symlink, path) { super('Cannot extract through symbolic link') this.path = path this.symlink = symlink } get name () { return 'SylinkError' } } class CwdError extends Error { constructor (path, code) { super(code + ': Cannot cd into \'' + path + '\'') this.path = path this.code = code } get name () { return 'CwdError' } } const cGet = (cache, key) => cache.get(normPath(key)) const cSet = (cache, key, val) => cache.set(normPath(key), val) const checkCwd = (dir, cb) => { fs.stat(dir, (er, st) => { if (er || !st.isDirectory()) { er = new CwdError(dir, er && er.code || 'ENOTDIR') } cb(er) }) } module.exports = (dir, opt, cb) => { dir = normPath(dir) // if there's any overlap between mask and mode, // then we'll need an explicit chmod const umask = opt.umask const mode = opt.mode | 0o0700 const needChmod = (mode & umask) !== 0 const uid = opt.uid const gid = opt.gid const doChown = typeof uid === 'number' && typeof gid === 'number' && (uid !== opt.processUid || gid !== opt.processGid) const preserve = opt.preserve const unlink = opt.unlink const cache = opt.cache const cwd = normPath(opt.cwd) const done = (er, created) => { if (er) { cb(er) } else { cSet(cache, dir, true) if (created && doChown) { chownr(created, uid, gid, er => done(er)) } else if (needChmod) { fs.chmod(dir, mode, cb) } else { cb() } } } if (cache && cGet(cache, dir) === true) { return done() } if (dir === cwd) { return checkCwd(dir, done) } if (preserve) { return mkdirp(dir, { mode }).then(made => done(null, made), done) } const sub = normPath(path.relative(cwd, dir)) const parts = sub.split('/') mkdir_(cwd, parts, mode, cache, unlink, cwd, null, done) } const mkdir_ = (base, parts, mode, cache, unlink, cwd, created, cb) => { if (!parts.length) { return cb(null, created) } const p = parts.shift() const part = normPath(path.resolve(base + '/' + p)) if (cGet(cache, part)) { return mkdir_(part, parts, mode, cache, unlink, cwd, created, cb) } fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb)) } const onmkdir = (part, parts, mode, cache, unlink, cwd, created, cb) => er => { if (er) { fs.lstat(part, (statEr, st) => { if (statEr) { statEr.path = statEr.path && normPath(statEr.path) cb(statEr) } else if (st.isDirectory()) { mkdir_(part, parts, mode, cache, unlink, cwd, created, cb) } else if (unlink) { fs.unlink(part, er => { if (er) { return cb(er) } fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb)) }) } else if (st.isSymbolicLink()) { return cb(new SymlinkError(part, part + '/' + parts.join('/'))) } else { cb(er) } }) } else { created = created || part mkdir_(part, parts, mode, cache, unlink, cwd, created, cb) } } const checkCwdSync = dir => { let ok = false let code = 'ENOTDIR' try { ok = fs.statSync(dir).isDirectory() } catch (er) { code = er.code } finally { if (!ok) { throw new CwdError(dir, code) } } } module.exports.sync = (dir, opt) => { dir = normPath(dir) // if there's any overlap between mask and mode, // then we'll need an explicit chmod const umask = opt.umask const mode = opt.mode | 0o0700 const needChmod = (mode & umask) !== 0 const uid = opt.uid const gid = opt.gid const doChown = typeof uid === 'number' && typeof gid === 'number' && (uid !== opt.processUid || gid !== opt.processGid) const preserve = opt.preserve const unlink = opt.unlink const cache = opt.cache const cwd = normPath(opt.cwd) const done = (created) => { cSet(cache, dir, true) if (created && doChown) { chownr.sync(created, uid, gid) } if (needChmod) { fs.chmodSync(dir, mode) } } if (cache && cGet(cache, dir) === true) { return done() } if (dir === cwd) { checkCwdSync(cwd) return done() } if (preserve) { return done(mkdirp.sync(dir, mode)) } const sub = normPath(path.relative(cwd, dir)) const parts = sub.split('/') let created = null for (let p = parts.shift(), part = cwd; p && (part += '/' + p); p = parts.shift()) { part = normPath(path.resolve(part)) if (cGet(cache, part)) { continue } try { fs.mkdirSync(part, mode) created = created || part cSet(cache, part, true) } catch (er) { const st = fs.lstatSync(part) if (st.isDirectory()) { cSet(cache, part, true) continue } else if (unlink) { fs.unlinkSync(part) fs.mkdirSync(part, mode) created = created || part cSet(cache, part, true) continue } else if (st.isSymbolicLink()) { return new SymlinkError(part, part + '/' + parts.join('/')) } } } return done(created) } LICENSE000064400000001375151701414140005555 0ustar00The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. index.js000064400000001253151701414140006210 0ustar00'use strict' // high-level commands exports.c = exports.create = require('./lib/create.js') exports.r = exports.replace = require('./lib/replace.js') exports.t = exports.list = require('./lib/list.js') exports.u = exports.update = require('./lib/update.js') exports.x = exports.extract = require('./lib/extract.js') // classes exports.Pack = require('./lib/pack.js') exports.Unpack = require('./lib/unpack.js') exports.Parse = require('./lib/parse.js') exports.ReadEntry = require('./lib/read-entry.js') exports.WriteEntry = require('./lib/write-entry.js') exports.Header = require('./lib/header.js') exports.Pax = require('./lib/pax.js') exports.types = require('./lib/types.js')
/home/emeraadmin/www/js/../node_modules/isobject/../d3-array/../function-bind/../../4d695/tar.tar