| 'use strict' |
| |
| const BB = require('bluebird') |
| |
| const chownr = BB.promisify(require('chownr')) |
| const mkdirp = BB.promisify(require('mkdirp')) |
| const inflight = require('promise-inflight') |
| const inferOwner = require('infer-owner') |
| |
| // Memoize getuid()/getgid() calls. |
| // patch process.setuid/setgid to invalidate cached value on change |
| const self = { uid: null, gid: null } |
| const getSelf = () => { |
| if (typeof self.uid !== 'number') { |
| self.uid = process.getuid() |
| const setuid = process.setuid |
| process.setuid = (uid) => { |
| self.uid = null |
| process.setuid = setuid |
| return process.setuid(uid) |
| } |
| } |
| if (typeof self.gid !== 'number') { |
| self.gid = process.getgid() |
| const setgid = process.setgid |
| process.setgid = (gid) => { |
| self.gid = null |
| process.setgid = setgid |
| return process.setgid(gid) |
| } |
| } |
| } |
| |
| module.exports.chownr = fixOwner |
| function fixOwner (cache, filepath) { |
| if (!process.getuid) { |
| // This platform doesn't need ownership fixing |
| return BB.resolve() |
| } |
| |
| getSelf() |
| if (self.uid !== 0) { |
| // almost certainly can't chown anyway |
| return BB.resolve() |
| } |
| |
| return BB.resolve(inferOwner(cache)).then(owner => { |
| const { uid, gid } = owner |
| |
| // No need to override if it's already what we used. |
| if (self.uid === uid && self.gid === gid) { |
| return |
| } |
| |
| return inflight( |
| 'fixOwner: fixing ownership on ' + filepath, |
| () => chownr( |
| filepath, |
| typeof uid === 'number' ? uid : self.uid, |
| typeof gid === 'number' ? gid : self.gid |
| ).catch({ code: 'ENOENT' }, () => null) |
| ) |
| }) |
| } |
| |
| module.exports.chownr.sync = fixOwnerSync |
| function fixOwnerSync (cache, filepath) { |
| if (!process.getuid) { |
| // This platform doesn't need ownership fixing |
| return |
| } |
| const { uid, gid } = inferOwner.sync(cache) |
| getSelf() |
| if (self.uid === uid && self.gid === gid) { |
| // No need to override if it's already what we used. |
| return |
| } |
| try { |
| chownr.sync( |
| filepath, |
| typeof uid === 'number' ? uid : self.uid, |
| typeof gid === 'number' ? gid : self.gid |
| ) |
| } catch (err) { |
| // only catch ENOENT, any other error is a problem. |
| if (err.code === 'ENOENT') { |
| return null |
| } |
| throw err |
| } |
| } |
| |
| module.exports.mkdirfix = mkdirfix |
| function mkdirfix (cache, p, cb) { |
| // we have to infer the owner _before_ making the directory, even though |
| // we aren't going to use the results, since the cache itself might not |
| // exist yet. If we mkdirp it, then our current uid/gid will be assumed |
| // to be correct if it creates the cache folder in the process. |
| return BB.resolve(inferOwner(cache)).then(() => { |
| return mkdirp(p).then(made => { |
| if (made) { |
| return fixOwner(cache, made).then(() => made) |
| } |
| }).catch({ code: 'EEXIST' }, () => { |
| // There's a race in mkdirp! |
| return fixOwner(cache, p).then(() => null) |
| }) |
| }) |
| } |
| |
| module.exports.mkdirfix.sync = mkdirfixSync |
| function mkdirfixSync (cache, p) { |
| try { |
| inferOwner.sync(cache) |
| const made = mkdirp.sync(p) |
| if (made) { |
| fixOwnerSync(cache, made) |
| return made |
| } |
| } catch (err) { |
| if (err.code === 'EEXIST') { |
| fixOwnerSync(cache, p) |
| return null |
| } else { |
| throw err |
| } |
| } |
| } |