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.
		
		
		
		
		
			
		
			
				
					547 lines
				
				15 KiB
			
		
		
			
		
	
	
					547 lines
				
				15 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'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
							 |