cache-file.js 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. // XXX use infer-owner or gentle-fs.mkdir here
  2. const npm = require('../npm.js')
  3. const path = require('path')
  4. const chownr = require('chownr')
  5. const writeFileAtomic = require('write-file-atomic')
  6. const mkdirp = require('mkdirp')
  7. const fs = require('graceful-fs')
  8. let cache = null
  9. let cacheUid = null
  10. let cacheGid = null
  11. let needChown = typeof process.getuid === 'function'
  12. const getCacheOwner = () => {
  13. let st
  14. try {
  15. st = fs.lstatSync(cache)
  16. } catch (er) {
  17. if (er.code !== 'ENOENT') {
  18. throw er
  19. }
  20. st = fs.lstatSync(path.dirname(cache))
  21. }
  22. cacheUid = st.uid
  23. cacheGid = st.gid
  24. needChown = st.uid !== process.getuid() ||
  25. st.gid !== process.getgid()
  26. }
  27. const writeOrAppend = (method, file, data) => {
  28. if (!cache) {
  29. cache = npm.config.get('cache')
  30. }
  31. // redundant if already absolute, but prevents non-absolute files
  32. // from being written as if they're part of the cache.
  33. file = path.resolve(cache, file)
  34. if (cacheUid === null && needChown) {
  35. getCacheOwner()
  36. }
  37. const dir = path.dirname(file)
  38. const firstMade = mkdirp.sync(dir)
  39. if (!needChown) {
  40. return method(file, data)
  41. }
  42. let methodThrew = true
  43. try {
  44. method(file, data)
  45. methodThrew = false
  46. } finally {
  47. // always try to leave it in the right ownership state, even on failure
  48. // let the method error fail it instead of the chownr error, though
  49. if (!methodThrew) {
  50. chownr.sync(firstMade || file, cacheUid, cacheGid)
  51. } else {
  52. try {
  53. chownr.sync(firstMade || file, cacheUid, cacheGid)
  54. } catch (_) {}
  55. }
  56. }
  57. }
  58. exports.append = (file, data) => writeOrAppend(fs.appendFileSync, file, data)
  59. exports.write = (file, data) => writeOrAppend(writeFileAtomic.sync, file, data)