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.
		
		
		
		
		
			
		
			
				
					230 lines
				
				5.4 KiB
			
		
		
			
		
	
	
					230 lines
				
				5.4 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'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)
							 | 
						||
| 
								 | 
							
								}
							 |