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