11cb0ef41Sopenharmony_ci'use strict' 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci// the PEND/UNPEND stuff tracks whether we're ready to emit end/close yet. 41cb0ef41Sopenharmony_ci// but the path reservations are required to avoid race conditions where 51cb0ef41Sopenharmony_ci// parallelized unpack ops may mess with one another, due to dependencies 61cb0ef41Sopenharmony_ci// (like a Link depending on its target) or destructive operations (like 71cb0ef41Sopenharmony_ci// clobbering an fs object to create one of a different type.) 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ciconst assert = require('assert') 101cb0ef41Sopenharmony_ciconst Parser = require('./parse.js') 111cb0ef41Sopenharmony_ciconst fs = require('fs') 121cb0ef41Sopenharmony_ciconst fsm = require('fs-minipass') 131cb0ef41Sopenharmony_ciconst path = require('path') 141cb0ef41Sopenharmony_ciconst mkdir = require('./mkdir.js') 151cb0ef41Sopenharmony_ciconst wc = require('./winchars.js') 161cb0ef41Sopenharmony_ciconst pathReservations = require('./path-reservations.js') 171cb0ef41Sopenharmony_ciconst stripAbsolutePath = require('./strip-absolute-path.js') 181cb0ef41Sopenharmony_ciconst normPath = require('./normalize-windows-path.js') 191cb0ef41Sopenharmony_ciconst stripSlash = require('./strip-trailing-slashes.js') 201cb0ef41Sopenharmony_ciconst normalize = require('./normalize-unicode.js') 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciconst ONENTRY = Symbol('onEntry') 231cb0ef41Sopenharmony_ciconst CHECKFS = Symbol('checkFs') 241cb0ef41Sopenharmony_ciconst CHECKFS2 = Symbol('checkFs2') 251cb0ef41Sopenharmony_ciconst PRUNECACHE = Symbol('pruneCache') 261cb0ef41Sopenharmony_ciconst ISREUSABLE = Symbol('isReusable') 271cb0ef41Sopenharmony_ciconst MAKEFS = Symbol('makeFs') 281cb0ef41Sopenharmony_ciconst FILE = Symbol('file') 291cb0ef41Sopenharmony_ciconst DIRECTORY = Symbol('directory') 301cb0ef41Sopenharmony_ciconst LINK = Symbol('link') 311cb0ef41Sopenharmony_ciconst SYMLINK = Symbol('symlink') 321cb0ef41Sopenharmony_ciconst HARDLINK = Symbol('hardlink') 331cb0ef41Sopenharmony_ciconst UNSUPPORTED = Symbol('unsupported') 341cb0ef41Sopenharmony_ciconst CHECKPATH = Symbol('checkPath') 351cb0ef41Sopenharmony_ciconst MKDIR = Symbol('mkdir') 361cb0ef41Sopenharmony_ciconst ONERROR = Symbol('onError') 371cb0ef41Sopenharmony_ciconst PENDING = Symbol('pending') 381cb0ef41Sopenharmony_ciconst PEND = Symbol('pend') 391cb0ef41Sopenharmony_ciconst UNPEND = Symbol('unpend') 401cb0ef41Sopenharmony_ciconst ENDED = Symbol('ended') 411cb0ef41Sopenharmony_ciconst MAYBECLOSE = Symbol('maybeClose') 421cb0ef41Sopenharmony_ciconst SKIP = Symbol('skip') 431cb0ef41Sopenharmony_ciconst DOCHOWN = Symbol('doChown') 441cb0ef41Sopenharmony_ciconst UID = Symbol('uid') 451cb0ef41Sopenharmony_ciconst GID = Symbol('gid') 461cb0ef41Sopenharmony_ciconst CHECKED_CWD = Symbol('checkedCwd') 471cb0ef41Sopenharmony_ciconst crypto = require('crypto') 481cb0ef41Sopenharmony_ciconst getFlag = require('./get-write-flag.js') 491cb0ef41Sopenharmony_ciconst platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform 501cb0ef41Sopenharmony_ciconst isWindows = platform === 'win32' 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci// Unlinks on Windows are not atomic. 531cb0ef41Sopenharmony_ci// 541cb0ef41Sopenharmony_ci// This means that if you have a file entry, followed by another 551cb0ef41Sopenharmony_ci// file entry with an identical name, and you cannot re-use the file 561cb0ef41Sopenharmony_ci// (because it's a hardlink, or because unlink:true is set, or it's 571cb0ef41Sopenharmony_ci// Windows, which does not have useful nlink values), then the unlink 581cb0ef41Sopenharmony_ci// will be committed to the disk AFTER the new file has been written 591cb0ef41Sopenharmony_ci// over the old one, deleting the new file. 601cb0ef41Sopenharmony_ci// 611cb0ef41Sopenharmony_ci// To work around this, on Windows systems, we rename the file and then 621cb0ef41Sopenharmony_ci// delete the renamed file. It's a sloppy kludge, but frankly, I do not 631cb0ef41Sopenharmony_ci// know of a better way to do this, given windows' non-atomic unlink 641cb0ef41Sopenharmony_ci// semantics. 651cb0ef41Sopenharmony_ci// 661cb0ef41Sopenharmony_ci// See: https://github.com/npm/node-tar/issues/183 671cb0ef41Sopenharmony_ci/* istanbul ignore next */ 681cb0ef41Sopenharmony_ciconst unlinkFile = (path, cb) => { 691cb0ef41Sopenharmony_ci if (!isWindows) { 701cb0ef41Sopenharmony_ci return fs.unlink(path, cb) 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci const name = path + '.DELETE.' + crypto.randomBytes(16).toString('hex') 741cb0ef41Sopenharmony_ci fs.rename(path, name, er => { 751cb0ef41Sopenharmony_ci if (er) { 761cb0ef41Sopenharmony_ci return cb(er) 771cb0ef41Sopenharmony_ci } 781cb0ef41Sopenharmony_ci fs.unlink(name, cb) 791cb0ef41Sopenharmony_ci }) 801cb0ef41Sopenharmony_ci} 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci/* istanbul ignore next */ 831cb0ef41Sopenharmony_ciconst unlinkFileSync = path => { 841cb0ef41Sopenharmony_ci if (!isWindows) { 851cb0ef41Sopenharmony_ci return fs.unlinkSync(path) 861cb0ef41Sopenharmony_ci } 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci const name = path + '.DELETE.' + crypto.randomBytes(16).toString('hex') 891cb0ef41Sopenharmony_ci fs.renameSync(path, name) 901cb0ef41Sopenharmony_ci fs.unlinkSync(name) 911cb0ef41Sopenharmony_ci} 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ci// this.gid, entry.gid, this.processUid 941cb0ef41Sopenharmony_ciconst uint32 = (a, b, c) => 951cb0ef41Sopenharmony_ci a === a >>> 0 ? a 961cb0ef41Sopenharmony_ci : b === b >>> 0 ? b 971cb0ef41Sopenharmony_ci : c 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci// clear the cache if it's a case-insensitive unicode-squashing match. 1001cb0ef41Sopenharmony_ci// we can't know if the current file system is case-sensitive or supports 1011cb0ef41Sopenharmony_ci// unicode fully, so we check for similarity on the maximally compatible 1021cb0ef41Sopenharmony_ci// representation. Err on the side of pruning, since all it's doing is 1031cb0ef41Sopenharmony_ci// preventing lstats, and it's not the end of the world if we get a false 1041cb0ef41Sopenharmony_ci// positive. 1051cb0ef41Sopenharmony_ci// Note that on windows, we always drop the entire cache whenever a 1061cb0ef41Sopenharmony_ci// symbolic link is encountered, because 8.3 filenames are impossible 1071cb0ef41Sopenharmony_ci// to reason about, and collisions are hazards rather than just failures. 1081cb0ef41Sopenharmony_ciconst cacheKeyNormalize = path => stripSlash(normPath(normalize(path))) 1091cb0ef41Sopenharmony_ci .toLowerCase() 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ciconst pruneCache = (cache, abs) => { 1121cb0ef41Sopenharmony_ci abs = cacheKeyNormalize(abs) 1131cb0ef41Sopenharmony_ci for (const path of cache.keys()) { 1141cb0ef41Sopenharmony_ci const pnorm = cacheKeyNormalize(path) 1151cb0ef41Sopenharmony_ci if (pnorm === abs || pnorm.indexOf(abs + '/') === 0) { 1161cb0ef41Sopenharmony_ci cache.delete(path) 1171cb0ef41Sopenharmony_ci } 1181cb0ef41Sopenharmony_ci } 1191cb0ef41Sopenharmony_ci} 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ciconst dropCache = cache => { 1221cb0ef41Sopenharmony_ci for (const key of cache.keys()) { 1231cb0ef41Sopenharmony_ci cache.delete(key) 1241cb0ef41Sopenharmony_ci } 1251cb0ef41Sopenharmony_ci} 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ciclass Unpack extends Parser { 1281cb0ef41Sopenharmony_ci constructor (opt) { 1291cb0ef41Sopenharmony_ci if (!opt) { 1301cb0ef41Sopenharmony_ci opt = {} 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci opt.ondone = _ => { 1341cb0ef41Sopenharmony_ci this[ENDED] = true 1351cb0ef41Sopenharmony_ci this[MAYBECLOSE]() 1361cb0ef41Sopenharmony_ci } 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci super(opt) 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci this[CHECKED_CWD] = false 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci this.reservations = pathReservations() 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci this.transform = typeof opt.transform === 'function' ? opt.transform : null 1451cb0ef41Sopenharmony_ci 1461cb0ef41Sopenharmony_ci this.writable = true 1471cb0ef41Sopenharmony_ci this.readable = false 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci this[PENDING] = 0 1501cb0ef41Sopenharmony_ci this[ENDED] = false 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci this.dirCache = opt.dirCache || new Map() 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci if (typeof opt.uid === 'number' || typeof opt.gid === 'number') { 1551cb0ef41Sopenharmony_ci // need both or neither 1561cb0ef41Sopenharmony_ci if (typeof opt.uid !== 'number' || typeof opt.gid !== 'number') { 1571cb0ef41Sopenharmony_ci throw new TypeError('cannot set owner without number uid and gid') 1581cb0ef41Sopenharmony_ci } 1591cb0ef41Sopenharmony_ci if (opt.preserveOwner) { 1601cb0ef41Sopenharmony_ci throw new TypeError( 1611cb0ef41Sopenharmony_ci 'cannot preserve owner in archive and also set owner explicitly') 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci this.uid = opt.uid 1641cb0ef41Sopenharmony_ci this.gid = opt.gid 1651cb0ef41Sopenharmony_ci this.setOwner = true 1661cb0ef41Sopenharmony_ci } else { 1671cb0ef41Sopenharmony_ci this.uid = null 1681cb0ef41Sopenharmony_ci this.gid = null 1691cb0ef41Sopenharmony_ci this.setOwner = false 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci // default true for root 1731cb0ef41Sopenharmony_ci if (opt.preserveOwner === undefined && typeof opt.uid !== 'number') { 1741cb0ef41Sopenharmony_ci this.preserveOwner = process.getuid && process.getuid() === 0 1751cb0ef41Sopenharmony_ci } else { 1761cb0ef41Sopenharmony_ci this.preserveOwner = !!opt.preserveOwner 1771cb0ef41Sopenharmony_ci } 1781cb0ef41Sopenharmony_ci 1791cb0ef41Sopenharmony_ci this.processUid = (this.preserveOwner || this.setOwner) && process.getuid ? 1801cb0ef41Sopenharmony_ci process.getuid() : null 1811cb0ef41Sopenharmony_ci this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? 1821cb0ef41Sopenharmony_ci process.getgid() : null 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci // mostly just for testing, but useful in some cases. 1851cb0ef41Sopenharmony_ci // Forcibly trigger a chown on every entry, no matter what 1861cb0ef41Sopenharmony_ci this.forceChown = opt.forceChown === true 1871cb0ef41Sopenharmony_ci 1881cb0ef41Sopenharmony_ci // turn ><?| in filenames into 0xf000-higher encoded forms 1891cb0ef41Sopenharmony_ci this.win32 = !!opt.win32 || isWindows 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci // do not unpack over files that are newer than what's in the archive 1921cb0ef41Sopenharmony_ci this.newer = !!opt.newer 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci // do not unpack over ANY files 1951cb0ef41Sopenharmony_ci this.keep = !!opt.keep 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ci // do not set mtime/atime of extracted entries 1981cb0ef41Sopenharmony_ci this.noMtime = !!opt.noMtime 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci // allow .., absolute path entries, and unpacking through symlinks 2011cb0ef41Sopenharmony_ci // without this, warn and skip .., relativize absolutes, and error 2021cb0ef41Sopenharmony_ci // on symlinks in extraction path 2031cb0ef41Sopenharmony_ci this.preservePaths = !!opt.preservePaths 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci // unlink files and links before writing. This breaks existing hard 2061cb0ef41Sopenharmony_ci // links, and removes symlink directories rather than erroring 2071cb0ef41Sopenharmony_ci this.unlink = !!opt.unlink 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci this.cwd = normPath(path.resolve(opt.cwd || process.cwd())) 2101cb0ef41Sopenharmony_ci this.strip = +opt.strip || 0 2111cb0ef41Sopenharmony_ci // if we're not chmodding, then we don't need the process umask 2121cb0ef41Sopenharmony_ci this.processUmask = opt.noChmod ? 0 : process.umask() 2131cb0ef41Sopenharmony_ci this.umask = typeof opt.umask === 'number' ? opt.umask : this.processUmask 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ci // default mode for dirs created as parents 2161cb0ef41Sopenharmony_ci this.dmode = opt.dmode || (0o0777 & (~this.umask)) 2171cb0ef41Sopenharmony_ci this.fmode = opt.fmode || (0o0666 & (~this.umask)) 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci this.on('entry', entry => this[ONENTRY](entry)) 2201cb0ef41Sopenharmony_ci } 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci // a bad or damaged archive is a warning for Parser, but an error 2231cb0ef41Sopenharmony_ci // when extracting. Mark those errors as unrecoverable, because 2241cb0ef41Sopenharmony_ci // the Unpack contract cannot be met. 2251cb0ef41Sopenharmony_ci warn (code, msg, data = {}) { 2261cb0ef41Sopenharmony_ci if (code === 'TAR_BAD_ARCHIVE' || code === 'TAR_ABORT') { 2271cb0ef41Sopenharmony_ci data.recoverable = false 2281cb0ef41Sopenharmony_ci } 2291cb0ef41Sopenharmony_ci return super.warn(code, msg, data) 2301cb0ef41Sopenharmony_ci } 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci [MAYBECLOSE] () { 2331cb0ef41Sopenharmony_ci if (this[ENDED] && this[PENDING] === 0) { 2341cb0ef41Sopenharmony_ci this.emit('prefinish') 2351cb0ef41Sopenharmony_ci this.emit('finish') 2361cb0ef41Sopenharmony_ci this.emit('end') 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci } 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci [CHECKPATH] (entry) { 2411cb0ef41Sopenharmony_ci if (this.strip) { 2421cb0ef41Sopenharmony_ci const parts = normPath(entry.path).split('/') 2431cb0ef41Sopenharmony_ci if (parts.length < this.strip) { 2441cb0ef41Sopenharmony_ci return false 2451cb0ef41Sopenharmony_ci } 2461cb0ef41Sopenharmony_ci entry.path = parts.slice(this.strip).join('/') 2471cb0ef41Sopenharmony_ci 2481cb0ef41Sopenharmony_ci if (entry.type === 'Link') { 2491cb0ef41Sopenharmony_ci const linkparts = normPath(entry.linkpath).split('/') 2501cb0ef41Sopenharmony_ci if (linkparts.length >= this.strip) { 2511cb0ef41Sopenharmony_ci entry.linkpath = linkparts.slice(this.strip).join('/') 2521cb0ef41Sopenharmony_ci } else { 2531cb0ef41Sopenharmony_ci return false 2541cb0ef41Sopenharmony_ci } 2551cb0ef41Sopenharmony_ci } 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci 2581cb0ef41Sopenharmony_ci if (!this.preservePaths) { 2591cb0ef41Sopenharmony_ci const p = normPath(entry.path) 2601cb0ef41Sopenharmony_ci const parts = p.split('/') 2611cb0ef41Sopenharmony_ci if (parts.includes('..') || isWindows && /^[a-z]:\.\.$/i.test(parts[0])) { 2621cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_ERROR', `path contains '..'`, { 2631cb0ef41Sopenharmony_ci entry, 2641cb0ef41Sopenharmony_ci path: p, 2651cb0ef41Sopenharmony_ci }) 2661cb0ef41Sopenharmony_ci return false 2671cb0ef41Sopenharmony_ci } 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci // strip off the root 2701cb0ef41Sopenharmony_ci const [root, stripped] = stripAbsolutePath(p) 2711cb0ef41Sopenharmony_ci if (root) { 2721cb0ef41Sopenharmony_ci entry.path = stripped 2731cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_INFO', `stripping ${root} from absolute path`, { 2741cb0ef41Sopenharmony_ci entry, 2751cb0ef41Sopenharmony_ci path: p, 2761cb0ef41Sopenharmony_ci }) 2771cb0ef41Sopenharmony_ci } 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci if (path.isAbsolute(entry.path)) { 2811cb0ef41Sopenharmony_ci entry.absolute = normPath(path.resolve(entry.path)) 2821cb0ef41Sopenharmony_ci } else { 2831cb0ef41Sopenharmony_ci entry.absolute = normPath(path.resolve(this.cwd, entry.path)) 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci 2861cb0ef41Sopenharmony_ci // if we somehow ended up with a path that escapes the cwd, and we are 2871cb0ef41Sopenharmony_ci // not in preservePaths mode, then something is fishy! This should have 2881cb0ef41Sopenharmony_ci // been prevented above, so ignore this for coverage. 2891cb0ef41Sopenharmony_ci /* istanbul ignore if - defense in depth */ 2901cb0ef41Sopenharmony_ci if (!this.preservePaths && 2911cb0ef41Sopenharmony_ci entry.absolute.indexOf(this.cwd + '/') !== 0 && 2921cb0ef41Sopenharmony_ci entry.absolute !== this.cwd) { 2931cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_ERROR', 'path escaped extraction target', { 2941cb0ef41Sopenharmony_ci entry, 2951cb0ef41Sopenharmony_ci path: normPath(entry.path), 2961cb0ef41Sopenharmony_ci resolvedPath: entry.absolute, 2971cb0ef41Sopenharmony_ci cwd: this.cwd, 2981cb0ef41Sopenharmony_ci }) 2991cb0ef41Sopenharmony_ci return false 3001cb0ef41Sopenharmony_ci } 3011cb0ef41Sopenharmony_ci 3021cb0ef41Sopenharmony_ci // an archive can set properties on the extraction directory, but it 3031cb0ef41Sopenharmony_ci // may not replace the cwd with a different kind of thing entirely. 3041cb0ef41Sopenharmony_ci if (entry.absolute === this.cwd && 3051cb0ef41Sopenharmony_ci entry.type !== 'Directory' && 3061cb0ef41Sopenharmony_ci entry.type !== 'GNUDumpDir') { 3071cb0ef41Sopenharmony_ci return false 3081cb0ef41Sopenharmony_ci } 3091cb0ef41Sopenharmony_ci 3101cb0ef41Sopenharmony_ci // only encode : chars that aren't drive letter indicators 3111cb0ef41Sopenharmony_ci if (this.win32) { 3121cb0ef41Sopenharmony_ci const { root: aRoot } = path.win32.parse(entry.absolute) 3131cb0ef41Sopenharmony_ci entry.absolute = aRoot + wc.encode(entry.absolute.slice(aRoot.length)) 3141cb0ef41Sopenharmony_ci const { root: pRoot } = path.win32.parse(entry.path) 3151cb0ef41Sopenharmony_ci entry.path = pRoot + wc.encode(entry.path.slice(pRoot.length)) 3161cb0ef41Sopenharmony_ci } 3171cb0ef41Sopenharmony_ci 3181cb0ef41Sopenharmony_ci return true 3191cb0ef41Sopenharmony_ci } 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci [ONENTRY] (entry) { 3221cb0ef41Sopenharmony_ci if (!this[CHECKPATH](entry)) { 3231cb0ef41Sopenharmony_ci return entry.resume() 3241cb0ef41Sopenharmony_ci } 3251cb0ef41Sopenharmony_ci 3261cb0ef41Sopenharmony_ci assert.equal(typeof entry.absolute, 'string') 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci switch (entry.type) { 3291cb0ef41Sopenharmony_ci case 'Directory': 3301cb0ef41Sopenharmony_ci case 'GNUDumpDir': 3311cb0ef41Sopenharmony_ci if (entry.mode) { 3321cb0ef41Sopenharmony_ci entry.mode = entry.mode | 0o700 3331cb0ef41Sopenharmony_ci } 3341cb0ef41Sopenharmony_ci 3351cb0ef41Sopenharmony_ci // eslint-disable-next-line no-fallthrough 3361cb0ef41Sopenharmony_ci case 'File': 3371cb0ef41Sopenharmony_ci case 'OldFile': 3381cb0ef41Sopenharmony_ci case 'ContiguousFile': 3391cb0ef41Sopenharmony_ci case 'Link': 3401cb0ef41Sopenharmony_ci case 'SymbolicLink': 3411cb0ef41Sopenharmony_ci return this[CHECKFS](entry) 3421cb0ef41Sopenharmony_ci 3431cb0ef41Sopenharmony_ci case 'CharacterDevice': 3441cb0ef41Sopenharmony_ci case 'BlockDevice': 3451cb0ef41Sopenharmony_ci case 'FIFO': 3461cb0ef41Sopenharmony_ci default: 3471cb0ef41Sopenharmony_ci return this[UNSUPPORTED](entry) 3481cb0ef41Sopenharmony_ci } 3491cb0ef41Sopenharmony_ci } 3501cb0ef41Sopenharmony_ci 3511cb0ef41Sopenharmony_ci [ONERROR] (er, entry) { 3521cb0ef41Sopenharmony_ci // Cwd has to exist, or else nothing works. That's serious. 3531cb0ef41Sopenharmony_ci // Other errors are warnings, which raise the error in strict 3541cb0ef41Sopenharmony_ci // mode, but otherwise continue on. 3551cb0ef41Sopenharmony_ci if (er.name === 'CwdError') { 3561cb0ef41Sopenharmony_ci this.emit('error', er) 3571cb0ef41Sopenharmony_ci } else { 3581cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_ERROR', er, { entry }) 3591cb0ef41Sopenharmony_ci this[UNPEND]() 3601cb0ef41Sopenharmony_ci entry.resume() 3611cb0ef41Sopenharmony_ci } 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci [MKDIR] (dir, mode, cb) { 3651cb0ef41Sopenharmony_ci mkdir(normPath(dir), { 3661cb0ef41Sopenharmony_ci uid: this.uid, 3671cb0ef41Sopenharmony_ci gid: this.gid, 3681cb0ef41Sopenharmony_ci processUid: this.processUid, 3691cb0ef41Sopenharmony_ci processGid: this.processGid, 3701cb0ef41Sopenharmony_ci umask: this.processUmask, 3711cb0ef41Sopenharmony_ci preserve: this.preservePaths, 3721cb0ef41Sopenharmony_ci unlink: this.unlink, 3731cb0ef41Sopenharmony_ci cache: this.dirCache, 3741cb0ef41Sopenharmony_ci cwd: this.cwd, 3751cb0ef41Sopenharmony_ci mode: mode, 3761cb0ef41Sopenharmony_ci noChmod: this.noChmod, 3771cb0ef41Sopenharmony_ci }, cb) 3781cb0ef41Sopenharmony_ci } 3791cb0ef41Sopenharmony_ci 3801cb0ef41Sopenharmony_ci [DOCHOWN] (entry) { 3811cb0ef41Sopenharmony_ci // in preserve owner mode, chown if the entry doesn't match process 3821cb0ef41Sopenharmony_ci // in set owner mode, chown if setting doesn't match process 3831cb0ef41Sopenharmony_ci return this.forceChown || 3841cb0ef41Sopenharmony_ci this.preserveOwner && 3851cb0ef41Sopenharmony_ci (typeof entry.uid === 'number' && entry.uid !== this.processUid || 3861cb0ef41Sopenharmony_ci typeof entry.gid === 'number' && entry.gid !== this.processGid) 3871cb0ef41Sopenharmony_ci || 3881cb0ef41Sopenharmony_ci (typeof this.uid === 'number' && this.uid !== this.processUid || 3891cb0ef41Sopenharmony_ci typeof this.gid === 'number' && this.gid !== this.processGid) 3901cb0ef41Sopenharmony_ci } 3911cb0ef41Sopenharmony_ci 3921cb0ef41Sopenharmony_ci [UID] (entry) { 3931cb0ef41Sopenharmony_ci return uint32(this.uid, entry.uid, this.processUid) 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci 3961cb0ef41Sopenharmony_ci [GID] (entry) { 3971cb0ef41Sopenharmony_ci return uint32(this.gid, entry.gid, this.processGid) 3981cb0ef41Sopenharmony_ci } 3991cb0ef41Sopenharmony_ci 4001cb0ef41Sopenharmony_ci [FILE] (entry, fullyDone) { 4011cb0ef41Sopenharmony_ci const mode = entry.mode & 0o7777 || this.fmode 4021cb0ef41Sopenharmony_ci const stream = new fsm.WriteStream(entry.absolute, { 4031cb0ef41Sopenharmony_ci flags: getFlag(entry.size), 4041cb0ef41Sopenharmony_ci mode: mode, 4051cb0ef41Sopenharmony_ci autoClose: false, 4061cb0ef41Sopenharmony_ci }) 4071cb0ef41Sopenharmony_ci stream.on('error', er => { 4081cb0ef41Sopenharmony_ci if (stream.fd) { 4091cb0ef41Sopenharmony_ci fs.close(stream.fd, () => {}) 4101cb0ef41Sopenharmony_ci } 4111cb0ef41Sopenharmony_ci 4121cb0ef41Sopenharmony_ci // flush all the data out so that we aren't left hanging 4131cb0ef41Sopenharmony_ci // if the error wasn't actually fatal. otherwise the parse 4141cb0ef41Sopenharmony_ci // is blocked, and we never proceed. 4151cb0ef41Sopenharmony_ci stream.write = () => true 4161cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 4171cb0ef41Sopenharmony_ci fullyDone() 4181cb0ef41Sopenharmony_ci }) 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_ci let actions = 1 4211cb0ef41Sopenharmony_ci const done = er => { 4221cb0ef41Sopenharmony_ci if (er) { 4231cb0ef41Sopenharmony_ci /* istanbul ignore else - we should always have a fd by now */ 4241cb0ef41Sopenharmony_ci if (stream.fd) { 4251cb0ef41Sopenharmony_ci fs.close(stream.fd, () => {}) 4261cb0ef41Sopenharmony_ci } 4271cb0ef41Sopenharmony_ci 4281cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 4291cb0ef41Sopenharmony_ci fullyDone() 4301cb0ef41Sopenharmony_ci return 4311cb0ef41Sopenharmony_ci } 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ci if (--actions === 0) { 4341cb0ef41Sopenharmony_ci fs.close(stream.fd, er => { 4351cb0ef41Sopenharmony_ci if (er) { 4361cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 4371cb0ef41Sopenharmony_ci } else { 4381cb0ef41Sopenharmony_ci this[UNPEND]() 4391cb0ef41Sopenharmony_ci } 4401cb0ef41Sopenharmony_ci fullyDone() 4411cb0ef41Sopenharmony_ci }) 4421cb0ef41Sopenharmony_ci } 4431cb0ef41Sopenharmony_ci } 4441cb0ef41Sopenharmony_ci 4451cb0ef41Sopenharmony_ci stream.on('finish', _ => { 4461cb0ef41Sopenharmony_ci // if futimes fails, try utimes 4471cb0ef41Sopenharmony_ci // if utimes fails, fail with the original error 4481cb0ef41Sopenharmony_ci // same for fchown/chown 4491cb0ef41Sopenharmony_ci const abs = entry.absolute 4501cb0ef41Sopenharmony_ci const fd = stream.fd 4511cb0ef41Sopenharmony_ci 4521cb0ef41Sopenharmony_ci if (entry.mtime && !this.noMtime) { 4531cb0ef41Sopenharmony_ci actions++ 4541cb0ef41Sopenharmony_ci const atime = entry.atime || new Date() 4551cb0ef41Sopenharmony_ci const mtime = entry.mtime 4561cb0ef41Sopenharmony_ci fs.futimes(fd, atime, mtime, er => 4571cb0ef41Sopenharmony_ci er ? fs.utimes(abs, atime, mtime, er2 => done(er2 && er)) 4581cb0ef41Sopenharmony_ci : done()) 4591cb0ef41Sopenharmony_ci } 4601cb0ef41Sopenharmony_ci 4611cb0ef41Sopenharmony_ci if (this[DOCHOWN](entry)) { 4621cb0ef41Sopenharmony_ci actions++ 4631cb0ef41Sopenharmony_ci const uid = this[UID](entry) 4641cb0ef41Sopenharmony_ci const gid = this[GID](entry) 4651cb0ef41Sopenharmony_ci fs.fchown(fd, uid, gid, er => 4661cb0ef41Sopenharmony_ci er ? fs.chown(abs, uid, gid, er2 => done(er2 && er)) 4671cb0ef41Sopenharmony_ci : done()) 4681cb0ef41Sopenharmony_ci } 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ci done() 4711cb0ef41Sopenharmony_ci }) 4721cb0ef41Sopenharmony_ci 4731cb0ef41Sopenharmony_ci const tx = this.transform ? this.transform(entry) || entry : entry 4741cb0ef41Sopenharmony_ci if (tx !== entry) { 4751cb0ef41Sopenharmony_ci tx.on('error', er => { 4761cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 4771cb0ef41Sopenharmony_ci fullyDone() 4781cb0ef41Sopenharmony_ci }) 4791cb0ef41Sopenharmony_ci entry.pipe(tx) 4801cb0ef41Sopenharmony_ci } 4811cb0ef41Sopenharmony_ci tx.pipe(stream) 4821cb0ef41Sopenharmony_ci } 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci [DIRECTORY] (entry, fullyDone) { 4851cb0ef41Sopenharmony_ci const mode = entry.mode & 0o7777 || this.dmode 4861cb0ef41Sopenharmony_ci this[MKDIR](entry.absolute, mode, er => { 4871cb0ef41Sopenharmony_ci if (er) { 4881cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 4891cb0ef41Sopenharmony_ci fullyDone() 4901cb0ef41Sopenharmony_ci return 4911cb0ef41Sopenharmony_ci } 4921cb0ef41Sopenharmony_ci 4931cb0ef41Sopenharmony_ci let actions = 1 4941cb0ef41Sopenharmony_ci const done = _ => { 4951cb0ef41Sopenharmony_ci if (--actions === 0) { 4961cb0ef41Sopenharmony_ci fullyDone() 4971cb0ef41Sopenharmony_ci this[UNPEND]() 4981cb0ef41Sopenharmony_ci entry.resume() 4991cb0ef41Sopenharmony_ci } 5001cb0ef41Sopenharmony_ci } 5011cb0ef41Sopenharmony_ci 5021cb0ef41Sopenharmony_ci if (entry.mtime && !this.noMtime) { 5031cb0ef41Sopenharmony_ci actions++ 5041cb0ef41Sopenharmony_ci fs.utimes(entry.absolute, entry.atime || new Date(), entry.mtime, done) 5051cb0ef41Sopenharmony_ci } 5061cb0ef41Sopenharmony_ci 5071cb0ef41Sopenharmony_ci if (this[DOCHOWN](entry)) { 5081cb0ef41Sopenharmony_ci actions++ 5091cb0ef41Sopenharmony_ci fs.chown(entry.absolute, this[UID](entry), this[GID](entry), done) 5101cb0ef41Sopenharmony_ci } 5111cb0ef41Sopenharmony_ci 5121cb0ef41Sopenharmony_ci done() 5131cb0ef41Sopenharmony_ci }) 5141cb0ef41Sopenharmony_ci } 5151cb0ef41Sopenharmony_ci 5161cb0ef41Sopenharmony_ci [UNSUPPORTED] (entry) { 5171cb0ef41Sopenharmony_ci entry.unsupported = true 5181cb0ef41Sopenharmony_ci this.warn('TAR_ENTRY_UNSUPPORTED', 5191cb0ef41Sopenharmony_ci `unsupported entry type: ${entry.type}`, { entry }) 5201cb0ef41Sopenharmony_ci entry.resume() 5211cb0ef41Sopenharmony_ci } 5221cb0ef41Sopenharmony_ci 5231cb0ef41Sopenharmony_ci [SYMLINK] (entry, done) { 5241cb0ef41Sopenharmony_ci this[LINK](entry, entry.linkpath, 'symlink', done) 5251cb0ef41Sopenharmony_ci } 5261cb0ef41Sopenharmony_ci 5271cb0ef41Sopenharmony_ci [HARDLINK] (entry, done) { 5281cb0ef41Sopenharmony_ci const linkpath = normPath(path.resolve(this.cwd, entry.linkpath)) 5291cb0ef41Sopenharmony_ci this[LINK](entry, linkpath, 'link', done) 5301cb0ef41Sopenharmony_ci } 5311cb0ef41Sopenharmony_ci 5321cb0ef41Sopenharmony_ci [PEND] () { 5331cb0ef41Sopenharmony_ci this[PENDING]++ 5341cb0ef41Sopenharmony_ci } 5351cb0ef41Sopenharmony_ci 5361cb0ef41Sopenharmony_ci [UNPEND] () { 5371cb0ef41Sopenharmony_ci this[PENDING]-- 5381cb0ef41Sopenharmony_ci this[MAYBECLOSE]() 5391cb0ef41Sopenharmony_ci } 5401cb0ef41Sopenharmony_ci 5411cb0ef41Sopenharmony_ci [SKIP] (entry) { 5421cb0ef41Sopenharmony_ci this[UNPEND]() 5431cb0ef41Sopenharmony_ci entry.resume() 5441cb0ef41Sopenharmony_ci } 5451cb0ef41Sopenharmony_ci 5461cb0ef41Sopenharmony_ci // Check if we can reuse an existing filesystem entry safely and 5471cb0ef41Sopenharmony_ci // overwrite it, rather than unlinking and recreating 5481cb0ef41Sopenharmony_ci // Windows doesn't report a useful nlink, so we just never reuse entries 5491cb0ef41Sopenharmony_ci [ISREUSABLE] (entry, st) { 5501cb0ef41Sopenharmony_ci return entry.type === 'File' && 5511cb0ef41Sopenharmony_ci !this.unlink && 5521cb0ef41Sopenharmony_ci st.isFile() && 5531cb0ef41Sopenharmony_ci st.nlink <= 1 && 5541cb0ef41Sopenharmony_ci !isWindows 5551cb0ef41Sopenharmony_ci } 5561cb0ef41Sopenharmony_ci 5571cb0ef41Sopenharmony_ci // check if a thing is there, and if so, try to clobber it 5581cb0ef41Sopenharmony_ci [CHECKFS] (entry) { 5591cb0ef41Sopenharmony_ci this[PEND]() 5601cb0ef41Sopenharmony_ci const paths = [entry.path] 5611cb0ef41Sopenharmony_ci if (entry.linkpath) { 5621cb0ef41Sopenharmony_ci paths.push(entry.linkpath) 5631cb0ef41Sopenharmony_ci } 5641cb0ef41Sopenharmony_ci this.reservations.reserve(paths, done => this[CHECKFS2](entry, done)) 5651cb0ef41Sopenharmony_ci } 5661cb0ef41Sopenharmony_ci 5671cb0ef41Sopenharmony_ci [PRUNECACHE] (entry) { 5681cb0ef41Sopenharmony_ci // if we are not creating a directory, and the path is in the dirCache, 5691cb0ef41Sopenharmony_ci // then that means we are about to delete the directory we created 5701cb0ef41Sopenharmony_ci // previously, and it is no longer going to be a directory, and neither 5711cb0ef41Sopenharmony_ci // is any of its children. 5721cb0ef41Sopenharmony_ci // If a symbolic link is encountered, all bets are off. There is no 5731cb0ef41Sopenharmony_ci // reasonable way to sanitize the cache in such a way we will be able to 5741cb0ef41Sopenharmony_ci // avoid having filesystem collisions. If this happens with a non-symlink 5751cb0ef41Sopenharmony_ci // entry, it'll just fail to unpack, but a symlink to a directory, using an 5761cb0ef41Sopenharmony_ci // 8.3 shortname or certain unicode attacks, can evade detection and lead 5771cb0ef41Sopenharmony_ci // to arbitrary writes to anywhere on the system. 5781cb0ef41Sopenharmony_ci if (entry.type === 'SymbolicLink') { 5791cb0ef41Sopenharmony_ci dropCache(this.dirCache) 5801cb0ef41Sopenharmony_ci } else if (entry.type !== 'Directory') { 5811cb0ef41Sopenharmony_ci pruneCache(this.dirCache, entry.absolute) 5821cb0ef41Sopenharmony_ci } 5831cb0ef41Sopenharmony_ci } 5841cb0ef41Sopenharmony_ci 5851cb0ef41Sopenharmony_ci [CHECKFS2] (entry, fullyDone) { 5861cb0ef41Sopenharmony_ci this[PRUNECACHE](entry) 5871cb0ef41Sopenharmony_ci 5881cb0ef41Sopenharmony_ci const done = er => { 5891cb0ef41Sopenharmony_ci this[PRUNECACHE](entry) 5901cb0ef41Sopenharmony_ci fullyDone(er) 5911cb0ef41Sopenharmony_ci } 5921cb0ef41Sopenharmony_ci 5931cb0ef41Sopenharmony_ci const checkCwd = () => { 5941cb0ef41Sopenharmony_ci this[MKDIR](this.cwd, this.dmode, er => { 5951cb0ef41Sopenharmony_ci if (er) { 5961cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 5971cb0ef41Sopenharmony_ci done() 5981cb0ef41Sopenharmony_ci return 5991cb0ef41Sopenharmony_ci } 6001cb0ef41Sopenharmony_ci this[CHECKED_CWD] = true 6011cb0ef41Sopenharmony_ci start() 6021cb0ef41Sopenharmony_ci }) 6031cb0ef41Sopenharmony_ci } 6041cb0ef41Sopenharmony_ci 6051cb0ef41Sopenharmony_ci const start = () => { 6061cb0ef41Sopenharmony_ci if (entry.absolute !== this.cwd) { 6071cb0ef41Sopenharmony_ci const parent = normPath(path.dirname(entry.absolute)) 6081cb0ef41Sopenharmony_ci if (parent !== this.cwd) { 6091cb0ef41Sopenharmony_ci return this[MKDIR](parent, this.dmode, er => { 6101cb0ef41Sopenharmony_ci if (er) { 6111cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 6121cb0ef41Sopenharmony_ci done() 6131cb0ef41Sopenharmony_ci return 6141cb0ef41Sopenharmony_ci } 6151cb0ef41Sopenharmony_ci afterMakeParent() 6161cb0ef41Sopenharmony_ci }) 6171cb0ef41Sopenharmony_ci } 6181cb0ef41Sopenharmony_ci } 6191cb0ef41Sopenharmony_ci afterMakeParent() 6201cb0ef41Sopenharmony_ci } 6211cb0ef41Sopenharmony_ci 6221cb0ef41Sopenharmony_ci const afterMakeParent = () => { 6231cb0ef41Sopenharmony_ci fs.lstat(entry.absolute, (lstatEr, st) => { 6241cb0ef41Sopenharmony_ci if (st && (this.keep || this.newer && st.mtime > entry.mtime)) { 6251cb0ef41Sopenharmony_ci this[SKIP](entry) 6261cb0ef41Sopenharmony_ci done() 6271cb0ef41Sopenharmony_ci return 6281cb0ef41Sopenharmony_ci } 6291cb0ef41Sopenharmony_ci if (lstatEr || this[ISREUSABLE](entry, st)) { 6301cb0ef41Sopenharmony_ci return this[MAKEFS](null, entry, done) 6311cb0ef41Sopenharmony_ci } 6321cb0ef41Sopenharmony_ci 6331cb0ef41Sopenharmony_ci if (st.isDirectory()) { 6341cb0ef41Sopenharmony_ci if (entry.type === 'Directory') { 6351cb0ef41Sopenharmony_ci const needChmod = !this.noChmod && 6361cb0ef41Sopenharmony_ci entry.mode && 6371cb0ef41Sopenharmony_ci (st.mode & 0o7777) !== entry.mode 6381cb0ef41Sopenharmony_ci const afterChmod = er => this[MAKEFS](er, entry, done) 6391cb0ef41Sopenharmony_ci if (!needChmod) { 6401cb0ef41Sopenharmony_ci return afterChmod() 6411cb0ef41Sopenharmony_ci } 6421cb0ef41Sopenharmony_ci return fs.chmod(entry.absolute, entry.mode, afterChmod) 6431cb0ef41Sopenharmony_ci } 6441cb0ef41Sopenharmony_ci // Not a dir entry, have to remove it. 6451cb0ef41Sopenharmony_ci // NB: the only way to end up with an entry that is the cwd 6461cb0ef41Sopenharmony_ci // itself, in such a way that == does not detect, is a 6471cb0ef41Sopenharmony_ci // tricky windows absolute path with UNC or 8.3 parts (and 6481cb0ef41Sopenharmony_ci // preservePaths:true, or else it will have been stripped). 6491cb0ef41Sopenharmony_ci // In that case, the user has opted out of path protections 6501cb0ef41Sopenharmony_ci // explicitly, so if they blow away the cwd, c'est la vie. 6511cb0ef41Sopenharmony_ci if (entry.absolute !== this.cwd) { 6521cb0ef41Sopenharmony_ci return fs.rmdir(entry.absolute, er => 6531cb0ef41Sopenharmony_ci this[MAKEFS](er, entry, done)) 6541cb0ef41Sopenharmony_ci } 6551cb0ef41Sopenharmony_ci } 6561cb0ef41Sopenharmony_ci 6571cb0ef41Sopenharmony_ci // not a dir, and not reusable 6581cb0ef41Sopenharmony_ci // don't remove if the cwd, we want that error 6591cb0ef41Sopenharmony_ci if (entry.absolute === this.cwd) { 6601cb0ef41Sopenharmony_ci return this[MAKEFS](null, entry, done) 6611cb0ef41Sopenharmony_ci } 6621cb0ef41Sopenharmony_ci 6631cb0ef41Sopenharmony_ci unlinkFile(entry.absolute, er => 6641cb0ef41Sopenharmony_ci this[MAKEFS](er, entry, done)) 6651cb0ef41Sopenharmony_ci }) 6661cb0ef41Sopenharmony_ci } 6671cb0ef41Sopenharmony_ci 6681cb0ef41Sopenharmony_ci if (this[CHECKED_CWD]) { 6691cb0ef41Sopenharmony_ci start() 6701cb0ef41Sopenharmony_ci } else { 6711cb0ef41Sopenharmony_ci checkCwd() 6721cb0ef41Sopenharmony_ci } 6731cb0ef41Sopenharmony_ci } 6741cb0ef41Sopenharmony_ci 6751cb0ef41Sopenharmony_ci [MAKEFS] (er, entry, done) { 6761cb0ef41Sopenharmony_ci if (er) { 6771cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 6781cb0ef41Sopenharmony_ci done() 6791cb0ef41Sopenharmony_ci return 6801cb0ef41Sopenharmony_ci } 6811cb0ef41Sopenharmony_ci 6821cb0ef41Sopenharmony_ci switch (entry.type) { 6831cb0ef41Sopenharmony_ci case 'File': 6841cb0ef41Sopenharmony_ci case 'OldFile': 6851cb0ef41Sopenharmony_ci case 'ContiguousFile': 6861cb0ef41Sopenharmony_ci return this[FILE](entry, done) 6871cb0ef41Sopenharmony_ci 6881cb0ef41Sopenharmony_ci case 'Link': 6891cb0ef41Sopenharmony_ci return this[HARDLINK](entry, done) 6901cb0ef41Sopenharmony_ci 6911cb0ef41Sopenharmony_ci case 'SymbolicLink': 6921cb0ef41Sopenharmony_ci return this[SYMLINK](entry, done) 6931cb0ef41Sopenharmony_ci 6941cb0ef41Sopenharmony_ci case 'Directory': 6951cb0ef41Sopenharmony_ci case 'GNUDumpDir': 6961cb0ef41Sopenharmony_ci return this[DIRECTORY](entry, done) 6971cb0ef41Sopenharmony_ci } 6981cb0ef41Sopenharmony_ci } 6991cb0ef41Sopenharmony_ci 7001cb0ef41Sopenharmony_ci [LINK] (entry, linkpath, link, done) { 7011cb0ef41Sopenharmony_ci // XXX: get the type ('symlink' or 'junction') for windows 7021cb0ef41Sopenharmony_ci fs[link](linkpath, entry.absolute, er => { 7031cb0ef41Sopenharmony_ci if (er) { 7041cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 7051cb0ef41Sopenharmony_ci } else { 7061cb0ef41Sopenharmony_ci this[UNPEND]() 7071cb0ef41Sopenharmony_ci entry.resume() 7081cb0ef41Sopenharmony_ci } 7091cb0ef41Sopenharmony_ci done() 7101cb0ef41Sopenharmony_ci }) 7111cb0ef41Sopenharmony_ci } 7121cb0ef41Sopenharmony_ci} 7131cb0ef41Sopenharmony_ci 7141cb0ef41Sopenharmony_ciconst callSync = fn => { 7151cb0ef41Sopenharmony_ci try { 7161cb0ef41Sopenharmony_ci return [null, fn()] 7171cb0ef41Sopenharmony_ci } catch (er) { 7181cb0ef41Sopenharmony_ci return [er, null] 7191cb0ef41Sopenharmony_ci } 7201cb0ef41Sopenharmony_ci} 7211cb0ef41Sopenharmony_ciclass UnpackSync extends Unpack { 7221cb0ef41Sopenharmony_ci [MAKEFS] (er, entry) { 7231cb0ef41Sopenharmony_ci return super[MAKEFS](er, entry, () => {}) 7241cb0ef41Sopenharmony_ci } 7251cb0ef41Sopenharmony_ci 7261cb0ef41Sopenharmony_ci [CHECKFS] (entry) { 7271cb0ef41Sopenharmony_ci this[PRUNECACHE](entry) 7281cb0ef41Sopenharmony_ci 7291cb0ef41Sopenharmony_ci if (!this[CHECKED_CWD]) { 7301cb0ef41Sopenharmony_ci const er = this[MKDIR](this.cwd, this.dmode) 7311cb0ef41Sopenharmony_ci if (er) { 7321cb0ef41Sopenharmony_ci return this[ONERROR](er, entry) 7331cb0ef41Sopenharmony_ci } 7341cb0ef41Sopenharmony_ci this[CHECKED_CWD] = true 7351cb0ef41Sopenharmony_ci } 7361cb0ef41Sopenharmony_ci 7371cb0ef41Sopenharmony_ci // don't bother to make the parent if the current entry is the cwd, 7381cb0ef41Sopenharmony_ci // we've already checked it. 7391cb0ef41Sopenharmony_ci if (entry.absolute !== this.cwd) { 7401cb0ef41Sopenharmony_ci const parent = normPath(path.dirname(entry.absolute)) 7411cb0ef41Sopenharmony_ci if (parent !== this.cwd) { 7421cb0ef41Sopenharmony_ci const mkParent = this[MKDIR](parent, this.dmode) 7431cb0ef41Sopenharmony_ci if (mkParent) { 7441cb0ef41Sopenharmony_ci return this[ONERROR](mkParent, entry) 7451cb0ef41Sopenharmony_ci } 7461cb0ef41Sopenharmony_ci } 7471cb0ef41Sopenharmony_ci } 7481cb0ef41Sopenharmony_ci 7491cb0ef41Sopenharmony_ci const [lstatEr, st] = callSync(() => fs.lstatSync(entry.absolute)) 7501cb0ef41Sopenharmony_ci if (st && (this.keep || this.newer && st.mtime > entry.mtime)) { 7511cb0ef41Sopenharmony_ci return this[SKIP](entry) 7521cb0ef41Sopenharmony_ci } 7531cb0ef41Sopenharmony_ci 7541cb0ef41Sopenharmony_ci if (lstatEr || this[ISREUSABLE](entry, st)) { 7551cb0ef41Sopenharmony_ci return this[MAKEFS](null, entry) 7561cb0ef41Sopenharmony_ci } 7571cb0ef41Sopenharmony_ci 7581cb0ef41Sopenharmony_ci if (st.isDirectory()) { 7591cb0ef41Sopenharmony_ci if (entry.type === 'Directory') { 7601cb0ef41Sopenharmony_ci const needChmod = !this.noChmod && 7611cb0ef41Sopenharmony_ci entry.mode && 7621cb0ef41Sopenharmony_ci (st.mode & 0o7777) !== entry.mode 7631cb0ef41Sopenharmony_ci const [er] = needChmod ? callSync(() => { 7641cb0ef41Sopenharmony_ci fs.chmodSync(entry.absolute, entry.mode) 7651cb0ef41Sopenharmony_ci }) : [] 7661cb0ef41Sopenharmony_ci return this[MAKEFS](er, entry) 7671cb0ef41Sopenharmony_ci } 7681cb0ef41Sopenharmony_ci // not a dir entry, have to remove it 7691cb0ef41Sopenharmony_ci const [er] = callSync(() => fs.rmdirSync(entry.absolute)) 7701cb0ef41Sopenharmony_ci this[MAKEFS](er, entry) 7711cb0ef41Sopenharmony_ci } 7721cb0ef41Sopenharmony_ci 7731cb0ef41Sopenharmony_ci // not a dir, and not reusable. 7741cb0ef41Sopenharmony_ci // don't remove if it's the cwd, since we want that error. 7751cb0ef41Sopenharmony_ci const [er] = entry.absolute === this.cwd ? [] 7761cb0ef41Sopenharmony_ci : callSync(() => unlinkFileSync(entry.absolute)) 7771cb0ef41Sopenharmony_ci this[MAKEFS](er, entry) 7781cb0ef41Sopenharmony_ci } 7791cb0ef41Sopenharmony_ci 7801cb0ef41Sopenharmony_ci [FILE] (entry, done) { 7811cb0ef41Sopenharmony_ci const mode = entry.mode & 0o7777 || this.fmode 7821cb0ef41Sopenharmony_ci 7831cb0ef41Sopenharmony_ci const oner = er => { 7841cb0ef41Sopenharmony_ci let closeError 7851cb0ef41Sopenharmony_ci try { 7861cb0ef41Sopenharmony_ci fs.closeSync(fd) 7871cb0ef41Sopenharmony_ci } catch (e) { 7881cb0ef41Sopenharmony_ci closeError = e 7891cb0ef41Sopenharmony_ci } 7901cb0ef41Sopenharmony_ci if (er || closeError) { 7911cb0ef41Sopenharmony_ci this[ONERROR](er || closeError, entry) 7921cb0ef41Sopenharmony_ci } 7931cb0ef41Sopenharmony_ci done() 7941cb0ef41Sopenharmony_ci } 7951cb0ef41Sopenharmony_ci 7961cb0ef41Sopenharmony_ci let fd 7971cb0ef41Sopenharmony_ci try { 7981cb0ef41Sopenharmony_ci fd = fs.openSync(entry.absolute, getFlag(entry.size), mode) 7991cb0ef41Sopenharmony_ci } catch (er) { 8001cb0ef41Sopenharmony_ci return oner(er) 8011cb0ef41Sopenharmony_ci } 8021cb0ef41Sopenharmony_ci const tx = this.transform ? this.transform(entry) || entry : entry 8031cb0ef41Sopenharmony_ci if (tx !== entry) { 8041cb0ef41Sopenharmony_ci tx.on('error', er => this[ONERROR](er, entry)) 8051cb0ef41Sopenharmony_ci entry.pipe(tx) 8061cb0ef41Sopenharmony_ci } 8071cb0ef41Sopenharmony_ci 8081cb0ef41Sopenharmony_ci tx.on('data', chunk => { 8091cb0ef41Sopenharmony_ci try { 8101cb0ef41Sopenharmony_ci fs.writeSync(fd, chunk, 0, chunk.length) 8111cb0ef41Sopenharmony_ci } catch (er) { 8121cb0ef41Sopenharmony_ci oner(er) 8131cb0ef41Sopenharmony_ci } 8141cb0ef41Sopenharmony_ci }) 8151cb0ef41Sopenharmony_ci 8161cb0ef41Sopenharmony_ci tx.on('end', _ => { 8171cb0ef41Sopenharmony_ci let er = null 8181cb0ef41Sopenharmony_ci // try both, falling futimes back to utimes 8191cb0ef41Sopenharmony_ci // if either fails, handle the first error 8201cb0ef41Sopenharmony_ci if (entry.mtime && !this.noMtime) { 8211cb0ef41Sopenharmony_ci const atime = entry.atime || new Date() 8221cb0ef41Sopenharmony_ci const mtime = entry.mtime 8231cb0ef41Sopenharmony_ci try { 8241cb0ef41Sopenharmony_ci fs.futimesSync(fd, atime, mtime) 8251cb0ef41Sopenharmony_ci } catch (futimeser) { 8261cb0ef41Sopenharmony_ci try { 8271cb0ef41Sopenharmony_ci fs.utimesSync(entry.absolute, atime, mtime) 8281cb0ef41Sopenharmony_ci } catch (utimeser) { 8291cb0ef41Sopenharmony_ci er = futimeser 8301cb0ef41Sopenharmony_ci } 8311cb0ef41Sopenharmony_ci } 8321cb0ef41Sopenharmony_ci } 8331cb0ef41Sopenharmony_ci 8341cb0ef41Sopenharmony_ci if (this[DOCHOWN](entry)) { 8351cb0ef41Sopenharmony_ci const uid = this[UID](entry) 8361cb0ef41Sopenharmony_ci const gid = this[GID](entry) 8371cb0ef41Sopenharmony_ci 8381cb0ef41Sopenharmony_ci try { 8391cb0ef41Sopenharmony_ci fs.fchownSync(fd, uid, gid) 8401cb0ef41Sopenharmony_ci } catch (fchowner) { 8411cb0ef41Sopenharmony_ci try { 8421cb0ef41Sopenharmony_ci fs.chownSync(entry.absolute, uid, gid) 8431cb0ef41Sopenharmony_ci } catch (chowner) { 8441cb0ef41Sopenharmony_ci er = er || fchowner 8451cb0ef41Sopenharmony_ci } 8461cb0ef41Sopenharmony_ci } 8471cb0ef41Sopenharmony_ci } 8481cb0ef41Sopenharmony_ci 8491cb0ef41Sopenharmony_ci oner(er) 8501cb0ef41Sopenharmony_ci }) 8511cb0ef41Sopenharmony_ci } 8521cb0ef41Sopenharmony_ci 8531cb0ef41Sopenharmony_ci [DIRECTORY] (entry, done) { 8541cb0ef41Sopenharmony_ci const mode = entry.mode & 0o7777 || this.dmode 8551cb0ef41Sopenharmony_ci const er = this[MKDIR](entry.absolute, mode) 8561cb0ef41Sopenharmony_ci if (er) { 8571cb0ef41Sopenharmony_ci this[ONERROR](er, entry) 8581cb0ef41Sopenharmony_ci done() 8591cb0ef41Sopenharmony_ci return 8601cb0ef41Sopenharmony_ci } 8611cb0ef41Sopenharmony_ci if (entry.mtime && !this.noMtime) { 8621cb0ef41Sopenharmony_ci try { 8631cb0ef41Sopenharmony_ci fs.utimesSync(entry.absolute, entry.atime || new Date(), entry.mtime) 8641cb0ef41Sopenharmony_ci } catch (er) {} 8651cb0ef41Sopenharmony_ci } 8661cb0ef41Sopenharmony_ci if (this[DOCHOWN](entry)) { 8671cb0ef41Sopenharmony_ci try { 8681cb0ef41Sopenharmony_ci fs.chownSync(entry.absolute, this[UID](entry), this[GID](entry)) 8691cb0ef41Sopenharmony_ci } catch (er) {} 8701cb0ef41Sopenharmony_ci } 8711cb0ef41Sopenharmony_ci done() 8721cb0ef41Sopenharmony_ci entry.resume() 8731cb0ef41Sopenharmony_ci } 8741cb0ef41Sopenharmony_ci 8751cb0ef41Sopenharmony_ci [MKDIR] (dir, mode) { 8761cb0ef41Sopenharmony_ci try { 8771cb0ef41Sopenharmony_ci return mkdir.sync(normPath(dir), { 8781cb0ef41Sopenharmony_ci uid: this.uid, 8791cb0ef41Sopenharmony_ci gid: this.gid, 8801cb0ef41Sopenharmony_ci processUid: this.processUid, 8811cb0ef41Sopenharmony_ci processGid: this.processGid, 8821cb0ef41Sopenharmony_ci umask: this.processUmask, 8831cb0ef41Sopenharmony_ci preserve: this.preservePaths, 8841cb0ef41Sopenharmony_ci unlink: this.unlink, 8851cb0ef41Sopenharmony_ci cache: this.dirCache, 8861cb0ef41Sopenharmony_ci cwd: this.cwd, 8871cb0ef41Sopenharmony_ci mode: mode, 8881cb0ef41Sopenharmony_ci }) 8891cb0ef41Sopenharmony_ci } catch (er) { 8901cb0ef41Sopenharmony_ci return er 8911cb0ef41Sopenharmony_ci } 8921cb0ef41Sopenharmony_ci } 8931cb0ef41Sopenharmony_ci 8941cb0ef41Sopenharmony_ci [LINK] (entry, linkpath, link, done) { 8951cb0ef41Sopenharmony_ci try { 8961cb0ef41Sopenharmony_ci fs[link + 'Sync'](linkpath, entry.absolute) 8971cb0ef41Sopenharmony_ci done() 8981cb0ef41Sopenharmony_ci entry.resume() 8991cb0ef41Sopenharmony_ci } catch (er) { 9001cb0ef41Sopenharmony_ci return this[ONERROR](er, entry) 9011cb0ef41Sopenharmony_ci } 9021cb0ef41Sopenharmony_ci } 9031cb0ef41Sopenharmony_ci} 9041cb0ef41Sopenharmony_ci 9051cb0ef41Sopenharmony_ciUnpack.Sync = UnpackSync 9061cb0ef41Sopenharmony_cimodule.exports = Unpack 907