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