summaryrefslogtreecommitdiff
path: root/deps/npm/lib/utils/tar.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/lib/utils/tar.js')
-rw-r--r--deps/npm/lib/utils/tar.js454
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))
-}