11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci// wrapper around mkdirp for tar's needs.
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ci// TODO: This should probably be a class, not functionally
51cb0ef41Sopenharmony_ci// passing around state in a gazillion args.
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst mkdirp = require('mkdirp')
81cb0ef41Sopenharmony_ciconst fs = require('fs')
91cb0ef41Sopenharmony_ciconst path = require('path')
101cb0ef41Sopenharmony_ciconst chownr = require('chownr')
111cb0ef41Sopenharmony_ciconst normPath = require('./normalize-windows-path.js')
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciclass SymlinkError extends Error {
141cb0ef41Sopenharmony_ci  constructor (symlink, path) {
151cb0ef41Sopenharmony_ci    super('Cannot extract through symbolic link')
161cb0ef41Sopenharmony_ci    this.path = path
171cb0ef41Sopenharmony_ci    this.symlink = symlink
181cb0ef41Sopenharmony_ci  }
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci  get name () {
211cb0ef41Sopenharmony_ci    return 'SylinkError'
221cb0ef41Sopenharmony_ci  }
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciclass CwdError extends Error {
261cb0ef41Sopenharmony_ci  constructor (path, code) {
271cb0ef41Sopenharmony_ci    super(code + ': Cannot cd into \'' + path + '\'')
281cb0ef41Sopenharmony_ci    this.path = path
291cb0ef41Sopenharmony_ci    this.code = code
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  get name () {
331cb0ef41Sopenharmony_ci    return 'CwdError'
341cb0ef41Sopenharmony_ci  }
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ciconst cGet = (cache, key) => cache.get(normPath(key))
381cb0ef41Sopenharmony_ciconst cSet = (cache, key, val) => cache.set(normPath(key), val)
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ciconst checkCwd = (dir, cb) => {
411cb0ef41Sopenharmony_ci  fs.stat(dir, (er, st) => {
421cb0ef41Sopenharmony_ci    if (er || !st.isDirectory()) {
431cb0ef41Sopenharmony_ci      er = new CwdError(dir, er && er.code || 'ENOTDIR')
441cb0ef41Sopenharmony_ci    }
451cb0ef41Sopenharmony_ci    cb(er)
461cb0ef41Sopenharmony_ci  })
471cb0ef41Sopenharmony_ci}
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_cimodule.exports = (dir, opt, cb) => {
501cb0ef41Sopenharmony_ci  dir = normPath(dir)
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  // if there's any overlap between mask and mode,
531cb0ef41Sopenharmony_ci  // then we'll need an explicit chmod
541cb0ef41Sopenharmony_ci  const umask = opt.umask
551cb0ef41Sopenharmony_ci  const mode = opt.mode | 0o0700
561cb0ef41Sopenharmony_ci  const needChmod = (mode & umask) !== 0
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci  const uid = opt.uid
591cb0ef41Sopenharmony_ci  const gid = opt.gid
601cb0ef41Sopenharmony_ci  const doChown = typeof uid === 'number' &&
611cb0ef41Sopenharmony_ci    typeof gid === 'number' &&
621cb0ef41Sopenharmony_ci    (uid !== opt.processUid || gid !== opt.processGid)
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci  const preserve = opt.preserve
651cb0ef41Sopenharmony_ci  const unlink = opt.unlink
661cb0ef41Sopenharmony_ci  const cache = opt.cache
671cb0ef41Sopenharmony_ci  const cwd = normPath(opt.cwd)
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci  const done = (er, created) => {
701cb0ef41Sopenharmony_ci    if (er) {
711cb0ef41Sopenharmony_ci      cb(er)
721cb0ef41Sopenharmony_ci    } else {
731cb0ef41Sopenharmony_ci      cSet(cache, dir, true)
741cb0ef41Sopenharmony_ci      if (created && doChown) {
751cb0ef41Sopenharmony_ci        chownr(created, uid, gid, er => done(er))
761cb0ef41Sopenharmony_ci      } else if (needChmod) {
771cb0ef41Sopenharmony_ci        fs.chmod(dir, mode, cb)
781cb0ef41Sopenharmony_ci      } else {
791cb0ef41Sopenharmony_ci        cb()
801cb0ef41Sopenharmony_ci      }
811cb0ef41Sopenharmony_ci    }
821cb0ef41Sopenharmony_ci  }
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  if (cache && cGet(cache, dir) === true) {
851cb0ef41Sopenharmony_ci    return done()
861cb0ef41Sopenharmony_ci  }
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  if (dir === cwd) {
891cb0ef41Sopenharmony_ci    return checkCwd(dir, done)
901cb0ef41Sopenharmony_ci  }
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci  if (preserve) {
931cb0ef41Sopenharmony_ci    return mkdirp(dir, { mode }).then(made => done(null, made), done)
941cb0ef41Sopenharmony_ci  }
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  const sub = normPath(path.relative(cwd, dir))
971cb0ef41Sopenharmony_ci  const parts = sub.split('/')
981cb0ef41Sopenharmony_ci  mkdir_(cwd, parts, mode, cache, unlink, cwd, null, done)
991cb0ef41Sopenharmony_ci}
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ciconst mkdir_ = (base, parts, mode, cache, unlink, cwd, created, cb) => {
1021cb0ef41Sopenharmony_ci  if (!parts.length) {
1031cb0ef41Sopenharmony_ci    return cb(null, created)
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci  const p = parts.shift()
1061cb0ef41Sopenharmony_ci  const part = normPath(path.resolve(base + '/' + p))
1071cb0ef41Sopenharmony_ci  if (cGet(cache, part)) {
1081cb0ef41Sopenharmony_ci    return mkdir_(part, parts, mode, cache, unlink, cwd, created, cb)
1091cb0ef41Sopenharmony_ci  }
1101cb0ef41Sopenharmony_ci  fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb))
1111cb0ef41Sopenharmony_ci}
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ciconst onmkdir = (part, parts, mode, cache, unlink, cwd, created, cb) => er => {
1141cb0ef41Sopenharmony_ci  if (er) {
1151cb0ef41Sopenharmony_ci    fs.lstat(part, (statEr, st) => {
1161cb0ef41Sopenharmony_ci      if (statEr) {
1171cb0ef41Sopenharmony_ci        statEr.path = statEr.path && normPath(statEr.path)
1181cb0ef41Sopenharmony_ci        cb(statEr)
1191cb0ef41Sopenharmony_ci      } else if (st.isDirectory()) {
1201cb0ef41Sopenharmony_ci        mkdir_(part, parts, mode, cache, unlink, cwd, created, cb)
1211cb0ef41Sopenharmony_ci      } else if (unlink) {
1221cb0ef41Sopenharmony_ci        fs.unlink(part, er => {
1231cb0ef41Sopenharmony_ci          if (er) {
1241cb0ef41Sopenharmony_ci            return cb(er)
1251cb0ef41Sopenharmony_ci          }
1261cb0ef41Sopenharmony_ci          fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb))
1271cb0ef41Sopenharmony_ci        })
1281cb0ef41Sopenharmony_ci      } else if (st.isSymbolicLink()) {
1291cb0ef41Sopenharmony_ci        return cb(new SymlinkError(part, part + '/' + parts.join('/')))
1301cb0ef41Sopenharmony_ci      } else {
1311cb0ef41Sopenharmony_ci        cb(er)
1321cb0ef41Sopenharmony_ci      }
1331cb0ef41Sopenharmony_ci    })
1341cb0ef41Sopenharmony_ci  } else {
1351cb0ef41Sopenharmony_ci    created = created || part
1361cb0ef41Sopenharmony_ci    mkdir_(part, parts, mode, cache, unlink, cwd, created, cb)
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci}
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ciconst checkCwdSync = dir => {
1411cb0ef41Sopenharmony_ci  let ok = false
1421cb0ef41Sopenharmony_ci  let code = 'ENOTDIR'
1431cb0ef41Sopenharmony_ci  try {
1441cb0ef41Sopenharmony_ci    ok = fs.statSync(dir).isDirectory()
1451cb0ef41Sopenharmony_ci  } catch (er) {
1461cb0ef41Sopenharmony_ci    code = er.code
1471cb0ef41Sopenharmony_ci  } finally {
1481cb0ef41Sopenharmony_ci    if (!ok) {
1491cb0ef41Sopenharmony_ci      throw new CwdError(dir, code)
1501cb0ef41Sopenharmony_ci    }
1511cb0ef41Sopenharmony_ci  }
1521cb0ef41Sopenharmony_ci}
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_cimodule.exports.sync = (dir, opt) => {
1551cb0ef41Sopenharmony_ci  dir = normPath(dir)
1561cb0ef41Sopenharmony_ci  // if there's any overlap between mask and mode,
1571cb0ef41Sopenharmony_ci  // then we'll need an explicit chmod
1581cb0ef41Sopenharmony_ci  const umask = opt.umask
1591cb0ef41Sopenharmony_ci  const mode = opt.mode | 0o0700
1601cb0ef41Sopenharmony_ci  const needChmod = (mode & umask) !== 0
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci  const uid = opt.uid
1631cb0ef41Sopenharmony_ci  const gid = opt.gid
1641cb0ef41Sopenharmony_ci  const doChown = typeof uid === 'number' &&
1651cb0ef41Sopenharmony_ci    typeof gid === 'number' &&
1661cb0ef41Sopenharmony_ci    (uid !== opt.processUid || gid !== opt.processGid)
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci  const preserve = opt.preserve
1691cb0ef41Sopenharmony_ci  const unlink = opt.unlink
1701cb0ef41Sopenharmony_ci  const cache = opt.cache
1711cb0ef41Sopenharmony_ci  const cwd = normPath(opt.cwd)
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci  const done = (created) => {
1741cb0ef41Sopenharmony_ci    cSet(cache, dir, true)
1751cb0ef41Sopenharmony_ci    if (created && doChown) {
1761cb0ef41Sopenharmony_ci      chownr.sync(created, uid, gid)
1771cb0ef41Sopenharmony_ci    }
1781cb0ef41Sopenharmony_ci    if (needChmod) {
1791cb0ef41Sopenharmony_ci      fs.chmodSync(dir, mode)
1801cb0ef41Sopenharmony_ci    }
1811cb0ef41Sopenharmony_ci  }
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci  if (cache && cGet(cache, dir) === true) {
1841cb0ef41Sopenharmony_ci    return done()
1851cb0ef41Sopenharmony_ci  }
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  if (dir === cwd) {
1881cb0ef41Sopenharmony_ci    checkCwdSync(cwd)
1891cb0ef41Sopenharmony_ci    return done()
1901cb0ef41Sopenharmony_ci  }
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  if (preserve) {
1931cb0ef41Sopenharmony_ci    return done(mkdirp.sync(dir, mode))
1941cb0ef41Sopenharmony_ci  }
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_ci  const sub = normPath(path.relative(cwd, dir))
1971cb0ef41Sopenharmony_ci  const parts = sub.split('/')
1981cb0ef41Sopenharmony_ci  let created = null
1991cb0ef41Sopenharmony_ci  for (let p = parts.shift(), part = cwd;
2001cb0ef41Sopenharmony_ci    p && (part += '/' + p);
2011cb0ef41Sopenharmony_ci    p = parts.shift()) {
2021cb0ef41Sopenharmony_ci    part = normPath(path.resolve(part))
2031cb0ef41Sopenharmony_ci    if (cGet(cache, part)) {
2041cb0ef41Sopenharmony_ci      continue
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci    try {
2081cb0ef41Sopenharmony_ci      fs.mkdirSync(part, mode)
2091cb0ef41Sopenharmony_ci      created = created || part
2101cb0ef41Sopenharmony_ci      cSet(cache, part, true)
2111cb0ef41Sopenharmony_ci    } catch (er) {
2121cb0ef41Sopenharmony_ci      const st = fs.lstatSync(part)
2131cb0ef41Sopenharmony_ci      if (st.isDirectory()) {
2141cb0ef41Sopenharmony_ci        cSet(cache, part, true)
2151cb0ef41Sopenharmony_ci        continue
2161cb0ef41Sopenharmony_ci      } else if (unlink) {
2171cb0ef41Sopenharmony_ci        fs.unlinkSync(part)
2181cb0ef41Sopenharmony_ci        fs.mkdirSync(part, mode)
2191cb0ef41Sopenharmony_ci        created = created || part
2201cb0ef41Sopenharmony_ci        cSet(cache, part, true)
2211cb0ef41Sopenharmony_ci        continue
2221cb0ef41Sopenharmony_ci      } else if (st.isSymbolicLink()) {
2231cb0ef41Sopenharmony_ci        return new SymlinkError(part, part + '/' + parts.join('/'))
2241cb0ef41Sopenharmony_ci      }
2251cb0ef41Sopenharmony_ci    }
2261cb0ef41Sopenharmony_ci  }
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci  return done(created)
2291cb0ef41Sopenharmony_ci}
230