PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/nodejs/node_modules/npm/lib/utils/tar.js

https://gitlab.com/pmuontains/Odoo
JavaScript | 352 lines | 285 code | 36 blank | 31 comment | 62 complexity | 13a360cc7e9eac962435559245d92fe9 MD5 | raw file
  1. // commands for packing and unpacking tarballs
  2. // this file is used by lib/cache.js
  3. var fs = require('graceful-fs')
  4. var path = require('path')
  5. var writeFileAtomic = require('write-file-atomic')
  6. var writeStreamAtomic = require('fs-write-stream-atomic')
  7. var log = require('npmlog')
  8. var uidNumber = require('uid-number')
  9. var readJson = require('read-package-json')
  10. var tar = require('tar')
  11. var zlib = require('zlib')
  12. var fstream = require('fstream')
  13. var Packer = require('fstream-npm')
  14. var iferr = require('iferr')
  15. var inherits = require('inherits')
  16. var npm = require('../npm.js')
  17. var rm = require('./gently-rm.js')
  18. var myUid = process.getuid && process.getuid()
  19. var myGid = process.getgid && process.getgid()
  20. var readPackageTree = require('read-package-tree')
  21. var union = require('lodash.union')
  22. var flattenTree = require('../install/flatten-tree.js')
  23. var moduleName = require('./module-name.js')
  24. var packageId = require('./package-id.js')
  25. if (process.env.SUDO_UID && myUid === 0) {
  26. if (!isNaN(process.env.SUDO_UID)) myUid = +process.env.SUDO_UID
  27. if (!isNaN(process.env.SUDO_GID)) myGid = +process.env.SUDO_GID
  28. }
  29. exports.pack = pack
  30. exports.unpack = unpack
  31. function pack (tarball, folder, pkg, cb) {
  32. log.verbose('tar pack', [tarball, folder])
  33. log.verbose('tarball', tarball)
  34. log.verbose('folder', folder)
  35. readJson(path.join(folder, 'package.json'), function (er, pkg) {
  36. if (er || !pkg.bundleDependencies) {
  37. pack_(tarball, folder, null, null, pkg, cb)
  38. } else {
  39. // we require this at runtime due to load-order issues, because recursive
  40. // requires fail if you replace the exports object, and we do, not in deps, but
  41. // in a dep of it.
  42. var recalculateMetadata = require('../install/deps.js').recalculateMetadata
  43. readPackageTree(folder, iferr(cb, function (tree) {
  44. recalculateMetadata(tree, log.newGroup('pack:' + pkg), iferr(cb, function () {
  45. pack_(tarball, folder, tree, flattenTree(tree), pkg, cb)
  46. }))
  47. }))
  48. }
  49. })
  50. }
  51. function BundledPacker (props) {
  52. Packer.call(this, props)
  53. }
  54. inherits(BundledPacker, Packer)
  55. function nameMatch (name) { return function (other) { return name === moduleName(other) } }
  56. function pack_ (tarball, folder, tree, flatTree, pkg, cb) {
  57. function InstancePacker (props) {
  58. BundledPacker.call(this, props)
  59. }
  60. inherits(InstancePacker, BundledPacker)
  61. InstancePacker.prototype.isBundled = function (name) {
  62. var bd = this.package && this.package.bundleDependencies
  63. if (!bd) return false
  64. if (!Array.isArray(bd)) {
  65. throw new Error(packageId(this) + '\'s `bundledDependencies` should ' +
  66. 'be an array')
  67. }
  68. if (!tree) return false
  69. if (bd.indexOf(name) !== -1) return true
  70. var pkg = tree.children.filter(nameMatch(name))[0]
  71. if (!pkg) return false
  72. var requiredBy = union([], pkg.package._requiredBy)
  73. var seen = {}
  74. while (requiredBy.length) {
  75. var req = requiredBy.shift()
  76. if (seen[req]) continue
  77. seen[req] = true
  78. var reqPkg = flatTree[req]
  79. if (!reqPkg) continue
  80. if (reqPkg.parent === tree && bd.indexOf(moduleName(reqPkg)) !== -1) {
  81. return true
  82. }
  83. requiredBy = union(requiredBy, reqPkg.package._requiredBy)
  84. }
  85. return false
  86. }
  87. new InstancePacker({ path: folder, type: 'Directory', isDirectory: true })
  88. .on('error', function (er) {
  89. if (er) log.error('tar pack', 'Error reading ' + folder)
  90. return cb(er)
  91. })
  92. // By default, npm includes some proprietary attributes in the
  93. // package tarball. This is sane, and allowed by the spec.
  94. // However, npm *itself* excludes these from its own package,
  95. // so that it can be more easily bootstrapped using old and
  96. // non-compliant tar implementations.
  97. .pipe(tar.Pack({ noProprietary: !npm.config.get('proprietary-attribs') }))
  98. .on('error', function (er) {
  99. if (er) log.error('tar.pack', 'tar creation error', tarball)
  100. cb(er)
  101. })
  102. .pipe(zlib.Gzip())
  103. .on('error', function (er) {
  104. if (er) log.error('tar.pack', 'gzip error ' + tarball)
  105. cb(er)
  106. })
  107. .pipe(writeStreamAtomic(tarball))
  108. .on('error', function (er) {
  109. if (er) log.error('tar.pack', 'Could not write ' + tarball)
  110. cb(er)
  111. })
  112. .on('close', cb)
  113. }
  114. function unpack (tarball, unpackTarget, dMode, fMode, uid, gid, cb) {
  115. log.verbose('tar', 'unpack', tarball)
  116. log.verbose('tar', 'unpacking to', unpackTarget)
  117. if (typeof cb !== 'function') {
  118. cb = gid
  119. gid = null
  120. }
  121. if (typeof cb !== 'function') {
  122. cb = uid
  123. uid = null
  124. }
  125. if (typeof cb !== 'function') {
  126. cb = fMode
  127. fMode = npm.modes.file
  128. }
  129. if (typeof cb !== 'function') {
  130. cb = dMode
  131. dMode = npm.modes.exec
  132. }
  133. uidNumber(uid, gid, function (er, uid, gid) {
  134. if (er) return cb(er)
  135. unpack_(tarball, unpackTarget, dMode, fMode, uid, gid, cb)
  136. })
  137. }
  138. function unpack_ (tarball, unpackTarget, dMode, fMode, uid, gid, cb) {
  139. rm(unpackTarget, function (er) {
  140. if (er) return cb(er)
  141. // gzip {tarball} --decompress --stdout \
  142. // | tar -mvxpf - --strip-components=1 -C {unpackTarget}
  143. gunzTarPerm(tarball, unpackTarget,
  144. dMode, fMode,
  145. uid, gid,
  146. function (er, folder) {
  147. if (er) return cb(er)
  148. readJson(path.resolve(folder, 'package.json'), cb)
  149. })
  150. })
  151. }
  152. function gunzTarPerm (tarball, target, dMode, fMode, uid, gid, cb_) {
  153. if (!dMode) dMode = npm.modes.exec
  154. if (!fMode) fMode = npm.modes.file
  155. log.silly('gunzTarPerm', 'modes', [dMode.toString(8), fMode.toString(8)])
  156. var cbCalled = false
  157. function cb (er) {
  158. if (cbCalled) return
  159. cbCalled = true
  160. cb_(er, target)
  161. }
  162. var fst = fs.createReadStream(tarball)
  163. fst.on('open', function (fd) {
  164. fs.fstat(fd, function (er, st) {
  165. if (er) return fst.emit('error', er)
  166. if (st.size === 0) {
  167. er = new Error('0-byte tarball\n' +
  168. 'Please run `npm cache clean`')
  169. fst.emit('error', er)
  170. }
  171. })
  172. })
  173. // figure out who we're supposed to be, if we're not pretending
  174. // to be a specific user.
  175. if (npm.config.get('unsafe-perm') && process.platform !== 'win32') {
  176. uid = myUid
  177. gid = myGid
  178. }
  179. function extractEntry (entry) {
  180. log.silly('gunzTarPerm', 'extractEntry', entry.path)
  181. // never create things that are user-unreadable,
  182. // or dirs that are user-un-listable. Only leads to headaches.
  183. var originalMode = entry.mode = entry.mode || entry.props.mode
  184. entry.mode = entry.mode | (entry.type === 'Directory' ? dMode : fMode)
  185. entry.mode = entry.mode & (~npm.modes.umask)
  186. entry.props.mode = entry.mode
  187. if (originalMode !== entry.mode) {
  188. log.silly('gunzTarPerm', 'modified mode',
  189. [entry.path, originalMode, entry.mode])
  190. }
  191. // if there's a specific owner uid/gid that we want, then set that
  192. if (process.platform !== 'win32' &&
  193. typeof uid === 'number' &&
  194. typeof gid === 'number') {
  195. entry.props.uid = entry.uid = uid
  196. entry.props.gid = entry.gid = gid
  197. }
  198. }
  199. var extractOpts = { type: 'Directory', path: target, strip: 1 }
  200. if (process.platform !== 'win32' &&
  201. typeof uid === 'number' &&
  202. typeof gid === 'number') {
  203. extractOpts.uid = uid
  204. extractOpts.gid = gid
  205. }
  206. var sawIgnores = {}
  207. extractOpts.filter = function () {
  208. // symbolic links are not allowed in packages.
  209. if (this.type.match(/^.*Link$/)) {
  210. log.warn('excluding symbolic link',
  211. this.path.substr(target.length + 1) +
  212. ' -> ' + this.linkpath)
  213. return false
  214. }
  215. // Note: This mirrors logic in the fs read operations that are
  216. // employed during tarball creation, in the fstream-npm module.
  217. // It is duplicated here to handle tarballs that are created
  218. // using other means, such as system tar or git archive.
  219. if (this.type === 'File') {
  220. var base = path.basename(this.path)
  221. if (base === '.npmignore') {
  222. sawIgnores[ this.path ] = true
  223. } else if (base === '.gitignore') {
  224. var npmignore = this.path.replace(/\.gitignore$/, '.npmignore')
  225. if (sawIgnores[npmignore]) {
  226. // Skip this one, already seen.
  227. return false
  228. } else {
  229. // Rename, may be clobbered later.
  230. this.path = npmignore
  231. this._path = npmignore
  232. }
  233. }
  234. }
  235. return true
  236. }
  237. fst
  238. .on('error', function (er) {
  239. if (er) log.error('tar.unpack', 'error reading ' + tarball)
  240. cb(er)
  241. })
  242. .on('data', function OD (c) {
  243. // detect what it is.
  244. // Then, depending on that, we'll figure out whether it's
  245. // a single-file module, gzipped tarball, or naked tarball.
  246. // gzipped files all start with 1f8b08
  247. if (c[0] === 0x1F &&
  248. c[1] === 0x8B &&
  249. c[2] === 0x08) {
  250. fst
  251. .pipe(zlib.Unzip())
  252. .on('error', function (er) {
  253. if (er) log.error('tar.unpack', 'unzip error ' + tarball)
  254. cb(er)
  255. })
  256. .pipe(tar.Extract(extractOpts))
  257. .on('entry', extractEntry)
  258. .on('error', function (er) {
  259. if (er) log.error('tar.unpack', 'untar error ' + tarball)
  260. cb(er)
  261. })
  262. .on('close', cb)
  263. } else if (hasTarHeader(c)) {
  264. // naked tar
  265. fst
  266. .pipe(tar.Extract(extractOpts))
  267. .on('entry', extractEntry)
  268. .on('error', function (er) {
  269. if (er) log.error('tar.unpack', 'untar error ' + tarball)
  270. cb(er)
  271. })
  272. .on('close', cb)
  273. } else {
  274. // naked js file
  275. var jsOpts = { path: path.resolve(target, 'index.js') }
  276. if (process.platform !== 'win32' &&
  277. typeof uid === 'number' &&
  278. typeof gid === 'number') {
  279. jsOpts.uid = uid
  280. jsOpts.gid = gid
  281. }
  282. fst
  283. .pipe(fstream.Writer(jsOpts))
  284. .on('error', function (er) {
  285. if (er) log.error('tar.unpack', 'copy error ' + tarball)
  286. cb(er)
  287. })
  288. .on('close', function () {
  289. var j = path.resolve(target, 'package.json')
  290. readJson(j, function (er, d) {
  291. if (er) {
  292. log.error('not a package', tarball)
  293. return cb(er)
  294. }
  295. writeFileAtomic(j, JSON.stringify(d) + '\n', cb)
  296. })
  297. })
  298. }
  299. // now un-hook, and re-emit the chunk
  300. fst.removeListener('data', OD)
  301. fst.emit('data', c)
  302. })
  303. }
  304. function hasTarHeader (c) {
  305. return c[257] === 0x75 && // tar archives have 7573746172 at position
  306. c[258] === 0x73 && // 257 and 003030 or 202000 at position 262
  307. c[259] === 0x74 &&
  308. c[260] === 0x61 &&
  309. c[261] === 0x72 &&
  310. ((c[262] === 0x00 &&
  311. c[263] === 0x30 &&
  312. c[264] === 0x30) ||
  313. (c[262] === 0x20 &&
  314. c[263] === 0x20 &&
  315. c[264] === 0x00))
  316. }