diff options
Diffstat (limited to 'deps/npm/lib/utils/tar.js')
-rw-r--r-- | deps/npm/lib/utils/tar.js | 454 |
1 files changed, 0 insertions, 454 deletions
diff --git a/deps/npm/lib/utils/tar.js b/deps/npm/lib/utils/tar.js deleted file mode 100644 index 12719e37e2..0000000000 --- a/deps/npm/lib/utils/tar.js +++ /dev/null @@ -1,454 +0,0 @@ -'use strict' - -// commands for packing and unpacking tarballs -// this file is used by lib/cache.js - -var fs = require('graceful-fs') -var path = require('path') -var writeFileAtomic = require('write-file-atomic') -var writeStreamAtomic = require('fs-write-stream-atomic') -var log = require('npmlog') -var uidNumber = require('uid-number') -var readJson = require('read-package-json') -var tar = require('tar') -var zlib = require('zlib') -var fstream = require('fstream') -var Packer = require('fstream-npm') -var iferr = require('iferr') -var inherits = require('inherits') -var npm = require('../npm.js') -var rm = require('./gently-rm.js') -var myUid = process.getuid && process.getuid() -var myGid = process.getgid && process.getgid() -var readPackageTree = require('read-package-tree') -var union = require('lodash.union') -var moduleName = require('./module-name.js') -var packageId = require('./package-id.js') -var pulseTillDone = require('../utils/pulse-till-done.js') - -if (process.env.SUDO_UID && myUid === 0) { - if (!isNaN(process.env.SUDO_UID)) myUid = +process.env.SUDO_UID - if (!isNaN(process.env.SUDO_GID)) myGid = +process.env.SUDO_GID -} - -exports.pack = pack -exports.unpack = unpack - -function pack (tarball, folder, pkg, cb) { - log.verbose('tar pack', [tarball, folder]) - - log.verbose('tarball', tarball) - log.verbose('folder', folder) - - readJson(path.join(folder, 'package.json'), function (er, pkg) { - if (er || !pkg.bundleDependencies) { - pack_(tarball, folder, null, pkg, cb) - } else { - // we require this at runtime due to load-order issues, because recursive - // requires fail if you replace the exports object, and we do, not in deps, but - // in a dep of it. - var computeMetadata = require('../install/deps.js').computeMetadata - - readPackageTree(folder, pulseTillDone('pack:readTree:' + packageId(pkg), iferr(cb, function (tree) { - computeMetadata(tree) - pack_(tarball, folder, tree, pkg, pulseTillDone('pack:' + packageId(pkg), cb)) - }))) - } - }) -} - -function BundledPacker (props) { - Packer.call(this, props) -} -inherits(BundledPacker, Packer) - -BundledPacker.prototype.applyIgnores = function (entry, partial, entryObj) { - if (!entryObj || entryObj.type !== 'Directory') { - // package.json files can never be ignored. - if (entry === 'package.json') return true - - // readme files should never be ignored. - if (entry.match(/^readme(\.[^\.]*)$/i)) return true - - // license files should never be ignored. - if (entry.match(/^(license|licence)(\.[^\.]*)?$/i)) return true - - // copyright notice files should never be ignored. - if (entry.match(/^(notice)(\.[^\.]*)?$/i)) return true - - // changelogs should never be ignored. - if (entry.match(/^(changes|changelog|history)(\.[^\.]*)?$/i)) return true - } - - // special rules. see below. - if (entry === 'node_modules' && this.packageRoot) return true - - // package.json main file should never be ignored. - var mainFile = this.package && this.package.main - if (mainFile && path.resolve(this.path, entry) === path.resolve(this.path, mainFile)) return true - - // some files are *never* allowed under any circumstances - // (VCS folders, native build cruft, npm cruft, regular cruft) - if (entry === '.git' || - entry === 'CVS' || - entry === '.svn' || - entry === '.hg' || - entry === '.lock-wscript' || - entry.match(/^\.wafpickle-[0-9]+$/) || - (this.parent && this.parent.packageRoot && this.basename === 'build' && - entry === 'config.gypi') || - entry === 'npm-debug.log' || - entry === '.npmrc' || - entry.match(/^\..*\.swp$/) || - entry === '.DS_Store' || - entry.match(/^\._/) || - entry.match(/^.*\.orig$/) || - // Package locks are never allowed in tarballs -- use shrinkwrap instead - entry === 'package-lock.json' - ) { - return false - } - - // in a node_modules folder, we only include bundled dependencies - // also, prevent packages in node_modules from being affected - // by rules set in the containing package, so that - // bundles don't get busted. - // Also, once in a bundle, everything is installed as-is - // To prevent infinite cycles in the case of cyclic deps that are - // linked with npm link, even in a bundle, deps are only bundled - // if they're not already present at a higher level. - if (this.bundleMagic) { - // bubbling up. stop here and allow anything the bundled pkg allows - if (entry.charAt(0) === '@') { - var firstSlash = entry.indexOf('/') - // continue to list the packages in this scope - if (firstSlash === -1) return true - - // bubbling up. stop here and allow anything the bundled pkg allows - if (entry.indexOf('/', firstSlash + 1) !== -1) return true - // bubbling up. stop here and allow anything the bundled pkg allows - } else if (entry.indexOf('/') !== -1) { - return true - } - - // never include the .bin. It's typically full of platform-specific - // stuff like symlinks and .cmd files anyway. - if (entry === '.bin') return false - - // the package root. - var p = this.parent - // the directory before this one. - var pp = p && p.parent - // the directory before that (if this is scoped) - if (pp && pp.basename[0] === '@') pp = pp && pp.parent - - // if this entry has already been bundled, and is a symlink, - // and it is the *same* symlink as this one, then exclude it. - if (pp && pp.bundleLinks && this.bundleLinks && - pp.bundleLinks[entry] && - pp.bundleLinks[entry] === this.bundleLinks[entry]) { - return false - } - - // since it's *not* a symbolic link, if we're *already* in a bundle, - // then we should include everything. - if (pp && pp.package && pp.basename === 'node_modules') { - return true - } - - // only include it at this point if it's a bundleDependency - return this.isBundled(entry) - } - // if (this.bundled) return true - - return Packer.prototype.applyIgnores.call(this, entry, partial, entryObj) -} - -function nameMatch (name) { return function (other) { return name === moduleName(other) } } - -function pack_ (tarball, folder, tree, pkg, cb) { - function InstancePacker (props) { - BundledPacker.call(this, props) - } - inherits(InstancePacker, BundledPacker) - InstancePacker.prototype.isBundled = function (name) { - var bd = this.package && this.package.bundleDependencies - if (!bd) return false - - if (!Array.isArray(bd)) { - throw new Error(packageId(this) + '\'s `bundledDependencies` should ' + - 'be an array') - } - if (!tree) return false - - if (bd.indexOf(name) !== -1) return true - var pkg = tree.children.filter(nameMatch(name))[0] - if (!pkg) return false - var requiredBy = [].concat(pkg.requiredBy) - var seen = new Set() - while (requiredBy.length) { - var reqPkg = requiredBy.shift() - if (seen.has(reqPkg)) continue - seen.add(reqPkg) - if (!reqPkg) continue - if (reqPkg.parent === tree && bd.indexOf(moduleName(reqPkg)) !== -1) { - return true - } - requiredBy = union(requiredBy, reqPkg.requiredBy) - } - return false - } - - new InstancePacker({ path: folder, type: 'Directory', isDirectory: true }) - .on('error', function (er) { - if (er) log.error('tar pack', 'Error reading ' + folder) - return cb(er) - }) - - // By default, npm includes some proprietary attributes in the - // package tarball. This is sane, and allowed by the spec. - // However, npm *itself* excludes these from its own package, - // so that it can be more easily bootstrapped using old and - // non-compliant tar implementations. - .pipe(tar.Pack({ noProprietary: !npm.config.get('proprietary-attribs') })) - .on('error', function (er) { - if (er) log.error('tar.pack', 'tar creation error', tarball) - cb(er) - }) - .pipe(zlib.Gzip()) - .on('error', function (er) { - if (er) log.error('tar.pack', 'gzip error ' + tarball) - cb(er) - }) - .pipe(writeStreamAtomic(tarball)) - .on('error', function (er) { - if (er) log.error('tar.pack', 'Could not write ' + tarball) - cb(er) - }) - .on('close', cb) -} - -function unpack (tarball, unpackTarget, dMode, fMode, uid, gid, cb) { - log.verbose('tar', 'unpack', tarball) - log.verbose('tar', 'unpacking to', unpackTarget) - if (typeof cb !== 'function') { - cb = gid - gid = null - } - if (typeof cb !== 'function') { - cb = uid - uid = null - } - if (typeof cb !== 'function') { - cb = fMode - fMode = npm.modes.file - } - if (typeof cb !== 'function') { - cb = dMode - dMode = npm.modes.exec - } - - uidNumber(uid, gid, function (er, uid, gid) { - if (er) return cb(er) - unpack_(tarball, unpackTarget, dMode, fMode, uid, gid, cb) - }) -} - -function unpack_ (tarball, unpackTarget, dMode, fMode, uid, gid, cb) { - rm(unpackTarget, function (er) { - if (er) return cb(er) - // gzip {tarball} --decompress --stdout \ - // | tar -mvxpf - --strip-components=1 -C {unpackTarget} - gunzTarPerm(tarball, unpackTarget, - dMode, fMode, - uid, gid, - function (er, folder) { - if (er) return cb(er) - readJson(path.resolve(folder, 'package.json'), cb) - }) - }) -} - -function gunzTarPerm (tarball, target, dMode, fMode, uid, gid, cb_) { - if (!dMode) dMode = npm.modes.exec - if (!fMode) fMode = npm.modes.file - log.silly('gunzTarPerm', 'modes', [dMode.toString(8), fMode.toString(8)]) - - var cbCalled = false - function cb (er) { - if (cbCalled) return - cbCalled = true - cb_(er, target) - } - - var fst = fs.createReadStream(tarball) - - fst.on('open', function (fd) { - fs.fstat(fd, function (er, st) { - if (er) return fst.emit('error', er) - if (st.size === 0) { - er = new Error('0-byte tarball\n' + - 'Please run `npm cache clean`') - fst.emit('error', er) - } - }) - }) - - // figure out who we're supposed to be, if we're not pretending - // to be a specific user. - if (npm.config.get('unsafe-perm') && process.platform !== 'win32') { - uid = myUid - gid = myGid - } - - function extractEntry (entry) { - log.silly('gunzTarPerm', 'extractEntry', entry.path) - // never create things that are user-unreadable, - // or dirs that are user-un-listable. Only leads to headaches. - var originalMode = entry.mode = entry.mode || entry.props.mode - entry.mode = entry.mode | (entry.type === 'Directory' ? dMode : fMode) - entry.mode = entry.mode & (~npm.modes.umask) - entry.props.mode = entry.mode - if (originalMode !== entry.mode) { - log.silly('gunzTarPerm', 'modified mode', - [entry.path, originalMode, entry.mode]) - } - - // if there's a specific owner uid/gid that we want, then set that - if (process.platform !== 'win32' && - typeof uid === 'number' && - typeof gid === 'number') { - entry.props.uid = entry.uid = uid - entry.props.gid = entry.gid = gid - } - } - - var extractOpts = { type: 'Directory', path: target, strip: 1 } - - if (process.platform !== 'win32' && - typeof uid === 'number' && - typeof gid === 'number') { - extractOpts.uid = uid - extractOpts.gid = gid - } - - var sawIgnores = {} - extractOpts.filter = function () { - // symbolic links are not allowed in packages. - if (this.type.match(/^.*Link$/)) { - log.warn('excluding symbolic link', - this.path.substr(target.length + 1) + - ' -> ' + this.linkpath) - return false - } - - // Note: This mirrors logic in the fs read operations that are - // employed during tarball creation, in the fstream-npm module. - // It is duplicated here to handle tarballs that are created - // using other means, such as system tar or git archive. - if (this.type === 'File') { - var base = path.basename(this.path) - if (base === '.npmignore') { - sawIgnores[ this.path ] = true - } else if (base === '.gitignore') { - var npmignore = this.path.replace(/\.gitignore$/, '.npmignore') - if (sawIgnores[npmignore]) { - // Skip this one, already seen. - return false - } else { - // Rename, may be clobbered later. - this.path = npmignore - this._path = npmignore - } - } - } - - return true - } - - fst - .on('error', function (er) { - if (er) log.error('tar.unpack', 'error reading ' + tarball) - cb(er) - }) - .on('data', function OD (c) { - // detect what it is. - // Then, depending on that, we'll figure out whether it's - // a single-file module, gzipped tarball, or naked tarball. - // gzipped files all start with 1f8b08 - if (c[0] === 0x1F && - c[1] === 0x8B && - c[2] === 0x08) { - fst - .pipe(zlib.Unzip()) - .on('error', function (er) { - if (er) log.error('tar.unpack', 'unzip error ' + tarball) - cb(er) - }) - .pipe(tar.Extract(extractOpts)) - .on('entry', extractEntry) - .on('error', function (er) { - if (er) log.error('tar.unpack', 'untar error ' + tarball) - cb(er) - }) - .on('close', cb) - } else if (hasTarHeader(c)) { - // naked tar - fst - .pipe(tar.Extract(extractOpts)) - .on('entry', extractEntry) - .on('error', function (er) { - if (er) log.error('tar.unpack', 'untar error ' + tarball) - cb(er) - }) - .on('close', cb) - } else { - // naked js file - var jsOpts = { path: path.resolve(target, 'index.js') } - - if (process.platform !== 'win32' && - typeof uid === 'number' && - typeof gid === 'number') { - jsOpts.uid = uid - jsOpts.gid = gid - } - - fst - .pipe(fstream.Writer(jsOpts)) - .on('error', function (er) { - if (er) log.error('tar.unpack', 'copy error ' + tarball) - cb(er) - }) - .on('close', function () { - var j = path.resolve(target, 'package.json') - readJson(j, function (er, d) { - if (er) { - log.error('not a package', tarball) - return cb(er) - } - writeFileAtomic(j, JSON.stringify(d) + '\n', cb) - }) - }) - } - - // now un-hook, and re-emit the chunk - fst.removeListener('data', OD) - fst.emit('data', c) - }) -} - -function hasTarHeader (c) { - return c[257] === 0x75 && // tar archives have 7573746172 at position - c[258] === 0x73 && // 257 and 003030 or 202000 at position 262 - c[259] === 0x74 && - c[260] === 0x61 && - c[261] === 0x72 && - - ((c[262] === 0x00 && - c[263] === 0x30 && - c[264] === 0x30) || - - (c[262] === 0x20 && - c[263] === 0x20 && - c[264] === 0x00)) -} |