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.
71 lines
1.7 KiB
71 lines
1.7 KiB
const cache = new Map() |
|
const fs = require('fs') |
|
const { dirname, resolve } = require('path') |
|
|
|
|
|
const lstat = path => new Promise((res, rej) => |
|
fs.lstat(path, (er, st) => er ? rej(er) : res(st))) |
|
|
|
const inferOwner = path => { |
|
path = resolve(path) |
|
if (cache.has(path)) |
|
return Promise.resolve(cache.get(path)) |
|
|
|
const statThen = st => { |
|
const { uid, gid } = st |
|
cache.set(path, { uid, gid }) |
|
return { uid, gid } |
|
} |
|
const parent = dirname(path) |
|
const parentTrap = parent === path ? null : er => { |
|
return inferOwner(parent).then((owner) => { |
|
cache.set(path, owner) |
|
return owner |
|
}) |
|
} |
|
return lstat(path).then(statThen, parentTrap) |
|
} |
|
|
|
const inferOwnerSync = path => { |
|
path = resolve(path) |
|
if (cache.has(path)) |
|
return cache.get(path) |
|
|
|
const parent = dirname(path) |
|
|
|
// avoid obscuring call site by re-throwing |
|
// "catch" the error by returning from a finally, |
|
// only if we're not at the root, and the parent call works. |
|
let threw = true |
|
try { |
|
const st = fs.lstatSync(path) |
|
threw = false |
|
const { uid, gid } = st |
|
cache.set(path, { uid, gid }) |
|
return { uid, gid } |
|
} finally { |
|
if (threw && parent !== path) { |
|
const owner = inferOwnerSync(parent) |
|
cache.set(path, owner) |
|
return owner // eslint-disable-line no-unsafe-finally |
|
} |
|
} |
|
} |
|
|
|
const inflight = new Map() |
|
module.exports = path => { |
|
path = resolve(path) |
|
if (inflight.has(path)) |
|
return Promise.resolve(inflight.get(path)) |
|
const p = inferOwner(path).then(owner => { |
|
inflight.delete(path) |
|
return owner |
|
}) |
|
inflight.set(path, p) |
|
return p |
|
} |
|
module.exports.sync = inferOwnerSync |
|
module.exports.clearCache = () => { |
|
cache.clear() |
|
inflight.clear() |
|
}
|
|
|