basant307/AI_Governance_Project
048
1var chownr = require('chownr')2var tar = require('tar-stream')3var pump = require('pump')4var mkdirp = require('mkdirp-classic')5var fs = require('fs')6var path = require('path')7var os = require('os')8 9var win32 = os.platform() === 'win32'10 11var noop = function () {}12 13var echo = function (name) {14 return name15}16 17var normalize = !win32 ? echo : function (name) {18 return name.replace(/\\/g, '/').replace(/[:?<>|]/g, '_')19}20 21var statAll = function (fs, stat, cwd, ignore, entries, sort) {22 var queue = entries || ['.']23 24 return function loop (callback) {25 if (!queue.length) return callback()26 var next = queue.shift()27 var nextAbs = path.join(cwd, next)28 29 stat.call(fs, nextAbs, function (err, stat) {30 if (err) return callback(err)31 32 if (!stat.isDirectory()) return callback(null, next, stat)33 34 fs.readdir(nextAbs, function (err, files) {35 if (err) return callback(err)36 37 if (sort) files.sort()38 for (var i = 0; i < files.length; i++) {39 if (!ignore(path.join(cwd, next, files[i]))) queue.push(path.join(next, files[i]))40 }41 42 callback(null, next, stat)43 })44 })45 }46}47 48var strip = function (map, level) {49 return function (header) {50 header.name = header.name.split('/').slice(level).join('/')51 52 var linkname = header.linkname53 if (linkname && (header.type === 'link' || path.isAbsolute(linkname))) {54 header.linkname = linkname.split('/').slice(level).join('/')55 }56 57 return map(header)58 }59}60 61exports.pack = function (cwd, opts) {62 if (!cwd) cwd = '.'63 if (!opts) opts = {}64 65 var xfs = opts.fs || fs66 var ignore = opts.ignore || opts.filter || noop67 var map = opts.map || noop68 var mapStream = opts.mapStream || echo69 var statNext = statAll(xfs, opts.dereference ? xfs.stat : xfs.lstat, cwd, ignore, opts.entries, opts.sort)70 var strict = opts.strict !== false71 var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask()72 var dmode = typeof opts.dmode === 'number' ? opts.dmode : 073 var fmode = typeof opts.fmode === 'number' ? opts.fmode : 074 var pack = opts.pack || tar.pack()75 var finish = opts.finish || noop76 77 if (opts.strip) map = strip(map, opts.strip)78 79 if (opts.readable) {80 dmode |= parseInt(555, 8)81 fmode |= parseInt(444, 8)82 }83 if (opts.writable) {84 dmode |= parseInt(333, 8)85 fmode |= parseInt(222, 8)86 }87 88 var onsymlink = function (filename, header) {89 xfs.readlink(path.join(cwd, filename), function (err, linkname) {90 if (err) return pack.destroy(err)91 header.linkname = normalize(linkname)92 pack.entry(header, onnextentry)93 })94 }95 96 var onstat = function (err, filename, stat) {97 if (err) return pack.destroy(err)98 if (!filename) {99 if (opts.finalize !== false) pack.finalize()100 return finish(pack)101 }102 103 if (stat.isSocket()) return onnextentry() // tar does not support sockets...104 105 var header = {106 name: normalize(filename),107 mode: (stat.mode | (stat.isDirectory() ? dmode : fmode)) & umask,108 mtime: stat.mtime,109 size: stat.size,110 type: 'file',111 uid: stat.uid,112 gid: stat.gid113 }114 115 if (stat.isDirectory()) {116 header.size = 0117 header.type = 'directory'118 header = map(header) || header119 return pack.entry(header, onnextentry)120 }121 122 if (stat.isSymbolicLink()) {123 header.size = 0124 header.type = 'symlink'125 header = map(header) || header126 return onsymlink(filename, header)127 }128 129 // TODO: add fifo etc...130 131 header = map(header) || header132 133 if (!stat.isFile()) {134 if (strict) return pack.destroy(new Error('unsupported type for ' + filename))135 return onnextentry()136 }137 138 var entry = pack.entry(header, onnextentry)139 if (!entry) return140 141 var rs = mapStream(xfs.createReadStream(path.join(cwd, filename), { start: 0, end: header.size > 0 ? header.size - 1 : header.size }), header)142 143 rs.on('error', function (err) { // always forward errors on destroy144 entry.destroy(err)145 })146 147 pump(rs, entry)148 }149 150 var onnextentry = function (err) {151 if (err) return pack.destroy(err)152 statNext(onstat)153 }154 155 onnextentry()156 157 return pack158}159 160var head = function (list) {161 return list.length ? list[list.length - 1] : null162}163 164var processGetuid = function () {165 return process.getuid ? process.getuid() : -1166}167 168var processUmask = function () {169 return process.umask ? process.umask() : 0170}171 172exports.extract = function (cwd, opts) {173 if (!cwd) cwd = '.'174 if (!opts) opts = {}175 176 var xfs = opts.fs || fs177 var ignore = opts.ignore || opts.filter || noop178 var map = opts.map || noop179 var mapStream = opts.mapStream || echo180 var own = opts.chown !== false && !win32 && processGetuid() === 0181 var extract = opts.extract || tar.extract()182 var stack = []183 var now = new Date()184 var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask()185 var dmode = typeof opts.dmode === 'number' ? opts.dmode : 0186 var fmode = typeof opts.fmode === 'number' ? opts.fmode : 0187 var strict = opts.strict !== false188 189 if (opts.strip) map = strip(map, opts.strip)190 191 if (opts.readable) {192 dmode |= parseInt(555, 8)193 fmode |= parseInt(444, 8)194 }195 if (opts.writable) {196 dmode |= parseInt(333, 8)197 fmode |= parseInt(222, 8)198 }199 200 var utimesParent = function (name, cb) { // we just set the mtime on the parent dir again everytime we write an entry201 var top202 while ((top = head(stack)) && name.slice(0, top[0].length) !== top[0]) stack.pop()203 if (!top) return cb()204 xfs.utimes(top[0], now, top[1], cb)205 }206 207 var utimes = function (name, header, cb) {208 if (opts.utimes === false) return cb()209 210 if (header.type === 'directory') return xfs.utimes(name, now, header.mtime, cb)211 if (header.type === 'symlink') return utimesParent(name, cb) // TODO: how to set mtime on link?212 213 xfs.utimes(name, now, header.mtime, function (err) {214 if (err) return cb(err)215 utimesParent(name, cb)216 })217 }218 219 var chperm = function (name, header, cb) {220 var link = header.type === 'symlink'221 222 /* eslint-disable node/no-deprecated-api */223 var chmod = link ? xfs.lchmod : xfs.chmod224 var chown = link ? xfs.lchown : xfs.chown225 /* eslint-enable node/no-deprecated-api */226 227 if (!chmod) return cb()228 229 var mode = (header.mode | (header.type === 'directory' ? dmode : fmode)) & umask230 231 if (chown && own) chown.call(xfs, name, header.uid, header.gid, onchown)232 else onchown(null)233 234 function onchown (err) {235 if (err) return cb(err)236 if (!chmod) return cb()237 chmod.call(xfs, name, mode, cb)238 }239 }240 241 extract.on('entry', function (header, stream, next) {242 header = map(header) || header243 header.name = normalize(header.name)244 var name = path.join(cwd, path.join('/', header.name))245 246 if (ignore(name, header)) {247 stream.resume()248 return next()249 }250 251 var stat = function (err) {252 if (err) return next(err)253 utimes(name, header, function (err) {254 if (err) return next(err)255 if (win32) return next()256 chperm(name, header, next)257 })258 }259 260 var onsymlink = function () {261 if (win32) return next() // skip symlinks on win for now before it can be tested262 xfs.unlink(name, function () {263 var dst = path.resolve(path.dirname(name), header.linkname)264 if (!inCwd(dst, cwd)) return next(new Error(name + ' is not a valid symlink'))265 266 xfs.symlink(header.linkname, name, stat)267 })268 }269 270 var onlink = function () {271 if (win32) return next() // skip links on win for now before it can be tested272 xfs.unlink(name, function () {273 var srcpath = path.join(cwd, path.join('/', header.linkname))274 275 xfs.realpath(srcpath, function (err, dst) {276 if (err || !inCwd(dst, cwd)) return next(new Error(name + ' is not a valid hardlink'))277 278 xfs.link(dst, name, function (err) {279 if (err && err.code === 'EPERM' && opts.hardlinkAsFilesFallback) {280 stream = xfs.createReadStream(srcpath)281 return onfile()282 }283 284 stat(err)285 })286 })287 })288 }289 290 var onfile = function () {291 var ws = xfs.createWriteStream(name)292 var rs = mapStream(stream, header)293 294 ws.on('error', function (err) { // always forward errors on destroy295 rs.destroy(err)296 })297 298 pump(rs, ws, function (err) {299 if (err) return next(err)300 ws.on('close', stat)301 })302 }303 304 if (header.type === 'directory') {305 stack.push([name, header.mtime])306 return mkdirfix(name, {307 fs: xfs, own: own, uid: header.uid, gid: header.gid308 }, stat)309 }310 311 var dir = path.dirname(name)312 313 validate(xfs, dir, path.join(cwd, '.'), function (err, valid) {314 if (err) return next(err)315 if (!valid) return next(new Error(dir + ' is not a valid path'))316 317 mkdirfix(dir, {318 fs: xfs, own: own, uid: header.uid, gid: header.gid319 }, function (err) {320 if (err) return next(err)321 322 switch (header.type) {323 case 'file': return onfile()324 case 'link': return onlink()325 case 'symlink': return onsymlink()326 }327 328 if (strict) return next(new Error('unsupported type for ' + name + ' (' + header.type + ')'))329 330 stream.resume()331 next()332 })333 })334 })335 336 if (opts.finish) extract.on('finish', opts.finish)337 338 return extract339}340 341function validate (fs, name, root, cb) {342 if (name === root) return cb(null, true)343 fs.lstat(name, function (err, st) {344 if (err && err.code !== 'ENOENT') return cb(err)345 if (err || st.isDirectory()) return validate(fs, path.join(name, '..'), root, cb)346 cb(null, false)347 })348}349 350function mkdirfix (name, opts, cb) {351 mkdirp(name, { fs: opts.fs }, function (err, made) {352 if (!err && made && opts.own) {353 chownr(made, opts.uid, opts.gid, cb)354 } else {355 cb(err)356 }357 })358}359 360function inCwd (dst, cwd) {361 cwd = path.resolve(cwd)362 return cwd === dst || dst.startsWith(cwd + path.sep)363}364 