You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					256 lines
				
				5.5 KiB
			
		
		
			
		
	
	
					256 lines
				
				5.5 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								var constants = require('fs-constants')
							 | 
						||
| 
								 | 
							
								var eos = require('end-of-stream')
							 | 
						||
| 
								 | 
							
								var inherits = require('inherits')
							 | 
						||
| 
								 | 
							
								var alloc = Buffer.alloc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Readable = require('readable-stream').Readable
							 | 
						||
| 
								 | 
							
								var Writable = require('readable-stream').Writable
							 | 
						||
| 
								 | 
							
								var StringDecoder = require('string_decoder').StringDecoder
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var headers = require('./headers')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var DMODE = parseInt('755', 8)
							 | 
						||
| 
								 | 
							
								var FMODE = parseInt('644', 8)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var END_OF_TAR = alloc(1024)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var noop = function () {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var overflow = function (self, size) {
							 | 
						||
| 
								 | 
							
								  size &= 511
							 | 
						||
| 
								 | 
							
								  if (size) self.push(END_OF_TAR.slice(0, 512 - size))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function modeToType (mode) {
							 | 
						||
| 
								 | 
							
								  switch (mode & constants.S_IFMT) {
							 | 
						||
| 
								 | 
							
								    case constants.S_IFBLK: return 'block-device'
							 | 
						||
| 
								 | 
							
								    case constants.S_IFCHR: return 'character-device'
							 | 
						||
| 
								 | 
							
								    case constants.S_IFDIR: return 'directory'
							 | 
						||
| 
								 | 
							
								    case constants.S_IFIFO: return 'fifo'
							 | 
						||
| 
								 | 
							
								    case constants.S_IFLNK: return 'symlink'
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return 'file'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Sink = function (to) {
							 | 
						||
| 
								 | 
							
								  Writable.call(this)
							 | 
						||
| 
								 | 
							
								  this.written = 0
							 | 
						||
| 
								 | 
							
								  this._to = to
							 | 
						||
| 
								 | 
							
								  this._destroyed = false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(Sink, Writable)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Sink.prototype._write = function (data, enc, cb) {
							 | 
						||
| 
								 | 
							
								  this.written += data.length
							 | 
						||
| 
								 | 
							
								  if (this._to.push(data)) return cb()
							 | 
						||
| 
								 | 
							
								  this._to._drain = cb
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Sink.prototype.destroy = function () {
							 | 
						||
| 
								 | 
							
								  if (this._destroyed) return
							 | 
						||
| 
								 | 
							
								  this._destroyed = true
							 | 
						||
| 
								 | 
							
								  this.emit('close')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var LinkSink = function () {
							 | 
						||
| 
								 | 
							
								  Writable.call(this)
							 | 
						||
| 
								 | 
							
								  this.linkname = ''
							 | 
						||
| 
								 | 
							
								  this._decoder = new StringDecoder('utf-8')
							 | 
						||
| 
								 | 
							
								  this._destroyed = false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(LinkSink, Writable)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LinkSink.prototype._write = function (data, enc, cb) {
							 | 
						||
| 
								 | 
							
								  this.linkname += this._decoder.write(data)
							 | 
						||
| 
								 | 
							
								  cb()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LinkSink.prototype.destroy = function () {
							 | 
						||
| 
								 | 
							
								  if (this._destroyed) return
							 | 
						||
| 
								 | 
							
								  this._destroyed = true
							 | 
						||
| 
								 | 
							
								  this.emit('close')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Void = function () {
							 | 
						||
| 
								 | 
							
								  Writable.call(this)
							 | 
						||
| 
								 | 
							
								  this._destroyed = false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(Void, Writable)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Void.prototype._write = function (data, enc, cb) {
							 | 
						||
| 
								 | 
							
								  cb(new Error('No body allowed for this entry'))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Void.prototype.destroy = function () {
							 | 
						||
| 
								 | 
							
								  if (this._destroyed) return
							 | 
						||
| 
								 | 
							
								  this._destroyed = true
							 | 
						||
| 
								 | 
							
								  this.emit('close')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Pack = function (opts) {
							 | 
						||
| 
								 | 
							
								  if (!(this instanceof Pack)) return new Pack(opts)
							 | 
						||
| 
								 | 
							
								  Readable.call(this, opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._drain = noop
							 | 
						||
| 
								 | 
							
								  this._finalized = false
							 | 
						||
| 
								 | 
							
								  this._finalizing = false
							 | 
						||
| 
								 | 
							
								  this._destroyed = false
							 | 
						||
| 
								 | 
							
								  this._stream = null
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(Pack, Readable)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype.entry = function (header, buffer, callback) {
							 | 
						||
| 
								 | 
							
								  if (this._stream) throw new Error('already piping an entry')
							 | 
						||
| 
								 | 
							
								  if (this._finalized || this._destroyed) return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof buffer === 'function') {
							 | 
						||
| 
								 | 
							
								    callback = buffer
							 | 
						||
| 
								 | 
							
								    buffer = null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!callback) callback = noop
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var self = this
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!header.size || header.type === 'symlink') header.size = 0
							 | 
						||
| 
								 | 
							
								  if (!header.type) header.type = modeToType(header.mode)
							 | 
						||
| 
								 | 
							
								  if (!header.mode) header.mode = header.type === 'directory' ? DMODE : FMODE
							 | 
						||
| 
								 | 
							
								  if (!header.uid) header.uid = 0
							 | 
						||
| 
								 | 
							
								  if (!header.gid) header.gid = 0
							 | 
						||
| 
								 | 
							
								  if (!header.mtime) header.mtime = new Date()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof buffer === 'string') buffer = Buffer.from(buffer)
							 | 
						||
| 
								 | 
							
								  if (Buffer.isBuffer(buffer)) {
							 | 
						||
| 
								 | 
							
								    header.size = buffer.length
							 | 
						||
| 
								 | 
							
								    this._encode(header)
							 | 
						||
| 
								 | 
							
								    var ok = this.push(buffer)
							 | 
						||
| 
								 | 
							
								    overflow(self, header.size)
							 | 
						||
| 
								 | 
							
								    if (ok) process.nextTick(callback)
							 | 
						||
| 
								 | 
							
								    else this._drain = callback
							 | 
						||
| 
								 | 
							
								    return new Void()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (header.type === 'symlink' && !header.linkname) {
							 | 
						||
| 
								 | 
							
								    var linkSink = new LinkSink()
							 | 
						||
| 
								 | 
							
								    eos(linkSink, function (err) {
							 | 
						||
| 
								 | 
							
								      if (err) { // stream was closed
							 | 
						||
| 
								 | 
							
								        self.destroy()
							 | 
						||
| 
								 | 
							
								        return callback(err)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      header.linkname = linkSink.linkname
							 | 
						||
| 
								 | 
							
								      self._encode(header)
							 | 
						||
| 
								 | 
							
								      callback()
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return linkSink
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._encode(header)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (header.type !== 'file' && header.type !== 'contiguous-file') {
							 | 
						||
| 
								 | 
							
								    process.nextTick(callback)
							 | 
						||
| 
								 | 
							
								    return new Void()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var sink = new Sink(this)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._stream = sink
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  eos(sink, function (err) {
							 | 
						||
| 
								 | 
							
								    self._stream = null
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (err) { // stream was closed
							 | 
						||
| 
								 | 
							
								      self.destroy()
							 | 
						||
| 
								 | 
							
								      return callback(err)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (sink.written !== header.size) { // corrupting tar
							 | 
						||
| 
								 | 
							
								      self.destroy()
							 | 
						||
| 
								 | 
							
								      return callback(new Error('size mismatch'))
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    overflow(self, header.size)
							 | 
						||
| 
								 | 
							
								    if (self._finalizing) self.finalize()
							 | 
						||
| 
								 | 
							
								    callback()
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return sink
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype.finalize = function () {
							 | 
						||
| 
								 | 
							
								  if (this._stream) {
							 | 
						||
| 
								 | 
							
								    this._finalizing = true
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._finalized) return
							 | 
						||
| 
								 | 
							
								  this._finalized = true
							 | 
						||
| 
								 | 
							
								  this.push(END_OF_TAR)
							 | 
						||
| 
								 | 
							
								  this.push(null)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype.destroy = function (err) {
							 | 
						||
| 
								 | 
							
								  if (this._destroyed) return
							 | 
						||
| 
								 | 
							
								  this._destroyed = true
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (err) this.emit('error', err)
							 | 
						||
| 
								 | 
							
								  this.emit('close')
							 | 
						||
| 
								 | 
							
								  if (this._stream && this._stream.destroy) this._stream.destroy()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype._encode = function (header) {
							 | 
						||
| 
								 | 
							
								  if (!header.pax) {
							 | 
						||
| 
								 | 
							
								    var buf = headers.encode(header)
							 | 
						||
| 
								 | 
							
								    if (buf) {
							 | 
						||
| 
								 | 
							
								      this.push(buf)
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  this._encodePax(header)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype._encodePax = function (header) {
							 | 
						||
| 
								 | 
							
								  var paxHeader = headers.encodePax({
							 | 
						||
| 
								 | 
							
								    name: header.name,
							 | 
						||
| 
								 | 
							
								    linkname: header.linkname,
							 | 
						||
| 
								 | 
							
								    pax: header.pax
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var newHeader = {
							 | 
						||
| 
								 | 
							
								    name: 'PaxHeader',
							 | 
						||
| 
								 | 
							
								    mode: header.mode,
							 | 
						||
| 
								 | 
							
								    uid: header.uid,
							 | 
						||
| 
								 | 
							
								    gid: header.gid,
							 | 
						||
| 
								 | 
							
								    size: paxHeader.length,
							 | 
						||
| 
								 | 
							
								    mtime: header.mtime,
							 | 
						||
| 
								 | 
							
								    type: 'pax-header',
							 | 
						||
| 
								 | 
							
								    linkname: header.linkname && 'PaxHeader',
							 | 
						||
| 
								 | 
							
								    uname: header.uname,
							 | 
						||
| 
								 | 
							
								    gname: header.gname,
							 | 
						||
| 
								 | 
							
								    devmajor: header.devmajor,
							 | 
						||
| 
								 | 
							
								    devminor: header.devminor
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.push(headers.encode(newHeader))
							 | 
						||
| 
								 | 
							
								  this.push(paxHeader)
							 | 
						||
| 
								 | 
							
								  overflow(this, paxHeader.length)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  newHeader.size = header.size
							 | 
						||
| 
								 | 
							
								  newHeader.type = header.type
							 | 
						||
| 
								 | 
							
								  this.push(headers.encode(newHeader))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Pack.prototype._read = function (n) {
							 | 
						||
| 
								 | 
							
								  var drain = this._drain
							 | 
						||
| 
								 | 
							
								  this._drain = noop
							 | 
						||
| 
								 | 
							
								  drain()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Pack
							 |