| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 | '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)}
 |