diff options
Diffstat (limited to 'deps/npm/node_modules/tar/lib/write-entry.js')
-rw-r--r-- | deps/npm/node_modules/tar/lib/write-entry.js | 203 |
1 files changed, 145 insertions, 58 deletions
diff --git a/deps/npm/node_modules/tar/lib/write-entry.js b/deps/npm/node_modules/tar/lib/write-entry.js index 0301759ad38..3702f2ae519 100644 --- a/deps/npm/node_modules/tar/lib/write-entry.js +++ b/deps/npm/node_modules/tar/lib/write-entry.js @@ -4,6 +4,15 @@ 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') @@ -21,6 +30,10 @@ 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') @@ -33,21 +46,31 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { super(opt) if (typeof p !== 'string') throw new TypeError('path is required') - this.path = p + 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() + 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 = opt.cwd || process.cwd() + 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) @@ -63,11 +86,13 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { 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 = opt.absolute || path.resolve(this.cwd, p) + this.absolute = normPath(opt.absolute || path.resolve(this.cwd, p)) if (this.path === '') this.path = './' @@ -85,6 +110,12 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { 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) @@ -117,13 +148,19 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { 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.path, - linkpath: this.linkpath, + 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), @@ -139,13 +176,14 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { }) if (this.header.encode() && !this.noPax) { - this.write(new Pax({ + 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.path, - linkpath: this.linkpath, + 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, @@ -154,7 +192,7 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { nlink: this.portable ? null : this.stat.nlink, }).encode()) } - this.write(this.header.block) + super.write(this.header.block) } [DIRECTORY] () { @@ -174,14 +212,14 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { } [ONREADLINK] (linkpath) { - this.linkpath = linkpath.replace(/\\/g, '/') + this.linkpath = normPath(linkpath) this[HEADER]() this.end() } [HARDLINK] (linkpath) { this.type = 'Link' - this.linkpath = path.relative(this.cwd, linkpath).replace(/\\/g, '/') + this.linkpath = normPath(path.relative(this.cwd, linkpath)) this.stat.size = 0 this[HEADER]() this.end() @@ -214,74 +252,110 @@ const WriteEntry = warner(class WriteEntry extends MiniPass { } [ONOPENFILE] (fd) { - const blockLen = 512 * Math.ceil(this.stat.size / 512) - const bufLen = Math.min(blockLen, this.maxReadSize) - const buf = Buffer.allocUnsafe(bufLen) - this[READ](fd, buf, 0, buf.length, 0, this.stat.size, blockLen) + 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] (fd, buf, offset, length, pos, remain, blockRemain) { + [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](fd, () => this.emit('error', er)) + return this[CLOSE](() => this.emit('error', er)) } - this[ONREAD](fd, buf, offset, length, pos, remain, blockRemain, bytesRead) + this[ONREAD](bytesRead) }) } - [CLOSE] (fd, cb) { - fs.close(fd, cb) + [CLOSE] (cb) { + fs.close(this.fd, cb) } - [ONREAD] (fd, buf, offset, length, pos, remain, blockRemain, bytesRead) { - if (bytesRead <= 0 && remain > 0) { + [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](fd, () => this.emit('error', er)) + return this[CLOSE](() => this.emit('error', er)) } - if (bytesRead > remain) { + 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](fd, () => this.emit('error', er)) + return this[CLOSE](() => this.emit('error', er)) } // null out the rest of the buffer, if we could fit the block padding - if (bytesRead === remain) { - for (let i = bytesRead; i < length && bytesRead < blockRemain; i++) { - buf[i + offset] = 0 + // 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++ - remain++ + this.remain++ } } - const writeBuf = offset === 0 && bytesRead === buf.length ? - buf : buf.slice(offset, offset + bytesRead) - remain -= bytesRead - blockRemain -= bytesRead - pos += bytesRead - offset += bytesRead + 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) + } - this.write(writeBuf) + 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) + } - if (!remain) { - if (blockRemain) - this.write(Buffer.alloc(blockRemain)) - return this[CLOSE](fd, er => er ? this.emit('error', er) : this.end()) + [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 (offset >= length) { - buf = Buffer.allocUnsafe(length) - offset = 0 + 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 } - length = buf.length - offset - this[READ](fd, buf, offset, length, pos, remain, blockRemain) + this.length = this.buf.length - this.offset + this[READ]() } }) @@ -298,25 +372,30 @@ class WriteEntrySync extends WriteEntry { this[ONOPENFILE](fs.openSync(this.absolute, 'r')) } - [READ] (fd, buf, offset, length, pos, remain, blockRemain) { + [READ] () { let threw = true try { + const { fd, buf, offset, length, pos } = this const bytesRead = fs.readSync(fd, buf, offset, length, pos) - this[ONREAD](fd, buf, offset, length, pos, remain, blockRemain, bytesRead) + 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](fd, () => {}) + this[CLOSE](() => {}) } catch (er) {} } } } - [CLOSE] (fd, cb) { - fs.closeSync(fd) + [AWAITDRAIN] (cb) { + cb() + } + + [CLOSE] (cb) { + fs.closeSync(this.fd) cb() } } @@ -336,7 +415,9 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { if (this.type === 'Directory' && this.portable) this.noMtime = true - this.path = readEntry.path + 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 @@ -346,7 +427,7 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { 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 = readEntry.linkpath + this.linkpath = normPath(readEntry.linkpath) if (typeof opt.onwarn === 'function') this.on('warn', opt.onwarn) @@ -364,8 +445,9 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { this.blockRemain = readEntry.startBlockSize this.header = new Header({ - path: this.path, - linkpath: this.linkpath, + 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, @@ -392,8 +474,9 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { ctime: this.portable ? null : this.ctime, gid: this.portable ? null : this.gid, mtime: this.noMtime ? null : this.mtime, - path: this.path, - linkpath: this.linkpath, + 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, @@ -407,6 +490,10 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { readEntry.pipe(this) } + [PREFIX] (path) { + return prefixPath(path, this.prefix) + } + [MODE] (mode) { return modeFix(mode, this.type === 'Directory', this.portable) } @@ -421,7 +508,7 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass { end () { if (this.blockRemain) - this.write(Buffer.alloc(this.blockRemain)) + super.write(Buffer.alloc(this.blockRemain)) return super.end() } }) |