You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
3.4 KiB
142 lines
3.4 KiB
'use strict' |
|
|
|
const util = require('util') |
|
|
|
const chownr = util.promisify(require('chownr')) |
|
const mkdirp = 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 Promise.resolve() |
|
} |
|
|
|
getSelf() |
|
if (self.uid !== 0) { |
|
// almost certainly can't chown anyway |
|
return Promise.resolve() |
|
} |
|
|
|
return Promise.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((err) => { |
|
if (err.code === 'ENOENT') |
|
return null |
|
|
|
throw err |
|
}) |
|
) |
|
}) |
|
} |
|
|
|
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 !== 0) { |
|
// almost certainly can't chown anyway |
|
return |
|
} |
|
|
|
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 Promise.resolve(inferOwner(cache)).then(() => { |
|
return mkdirp(p) |
|
.then((made) => { |
|
if (made) |
|
return fixOwner(cache, made).then(() => made) |
|
}) |
|
.catch((err) => { |
|
if (err.code === 'EEXIST') |
|
return fixOwner(cache, p).then(() => null) |
|
|
|
throw err |
|
}) |
|
}) |
|
} |
|
|
|
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 |
|
} |
|
}
|
|
|