1'use strict' 2 3const BB = require('bluebird') 4 5const chownr = BB.promisify(require('chownr')) 6const mkdirp = BB.promisify(require('mkdirp')) 7const inflight = require('promise-inflight') 8const inferOwner = require('infer-owner') 9 10// Memoize getuid()/getgid() calls. 11// patch process.setuid/setgid to invalidate cached value on change 12const self = { uid: null, gid: null } 13const getSelf = () => { 14 if (typeof self.uid !== 'number') { 15 self.uid = process.getuid() 16 const setuid = process.setuid 17 process.setuid = (uid) => { 18 self.uid = null 19 process.setuid = setuid 20 return process.setuid(uid) 21 } 22 } 23 if (typeof self.gid !== 'number') { 24 self.gid = process.getgid() 25 const setgid = process.setgid 26 process.setgid = (gid) => { 27 self.gid = null 28 process.setgid = setgid 29 return process.setgid(gid) 30 } 31 } 32} 33 34module.exports.chownr = fixOwner 35function fixOwner (cache, filepath) { 36 if (!process.getuid) { 37 // This platform doesn't need ownership fixing 38 return BB.resolve() 39 } 40 41 getSelf() 42 if (self.uid !== 0) { 43 // almost certainly can't chown anyway 44 return BB.resolve() 45 } 46 47 return BB.resolve(inferOwner(cache)).then(owner => { 48 const { uid, gid } = owner 49 50 // No need to override if it's already what we used. 51 if (self.uid === uid && self.gid === gid) { 52 return 53 } 54 55 return inflight( 56 'fixOwner: fixing ownership on ' + filepath, 57 () => chownr( 58 filepath, 59 typeof uid === 'number' ? uid : self.uid, 60 typeof gid === 'number' ? gid : self.gid 61 ).catch({ code: 'ENOENT' }, () => null) 62 ) 63 }) 64} 65 66module.exports.chownr.sync = fixOwnerSync 67function fixOwnerSync (cache, filepath) { 68 if (!process.getuid) { 69 // This platform doesn't need ownership fixing 70 return 71 } 72 const { uid, gid } = inferOwner.sync(cache) 73 getSelf() 74 if (self.uid === uid && self.gid === gid) { 75 // No need to override if it's already what we used. 76 return 77 } 78 try { 79 chownr.sync( 80 filepath, 81 typeof uid === 'number' ? uid : self.uid, 82 typeof gid === 'number' ? gid : self.gid 83 ) 84 } catch (err) { 85 // only catch ENOENT, any other error is a problem. 86 if (err.code === 'ENOENT') { 87 return null 88 } 89 throw err 90 } 91} 92 93module.exports.mkdirfix = mkdirfix 94function mkdirfix (cache, p, cb) { 95 // we have to infer the owner _before_ making the directory, even though 96 // we aren't going to use the results, since the cache itself might not 97 // exist yet. If we mkdirp it, then our current uid/gid will be assumed 98 // to be correct if it creates the cache folder in the process. 99 return BB.resolve(inferOwner(cache)).then(() => { 100 return mkdirp(p).then(made => { 101 if (made) { 102 return fixOwner(cache, made).then(() => made) 103 } 104 }).catch({ code: 'EEXIST' }, () => { 105 // There's a race in mkdirp! 106 return fixOwner(cache, p).then(() => null) 107 }) 108 }) 109} 110 111module.exports.mkdirfix.sync = mkdirfixSync 112function mkdirfixSync (cache, p) { 113 try { 114 inferOwner.sync(cache) 115 const made = mkdirp.sync(p) 116 if (made) { 117 fixOwnerSync(cache, made) 118 return made 119 } 120 } catch (err) { 121 if (err.code === 'EEXIST') { 122 fixOwnerSync(cache, p) 123 return null 124 } else { 125 throw err 126 } 127 } 128} 129