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