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.

index.js 1.7KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. const cache = new Map()
  2. const fs = require('fs')
  3. const { dirname, resolve } = require('path')
  4. const lstat = path => new Promise((res, rej) =>
  5. fs.lstat(path, (er, st) => er ? rej(er) : res(st)))
  6. const inferOwner = path => {
  7. path = resolve(path)
  8. if (cache.has(path))
  9. return Promise.resolve(cache.get(path))
  10. const statThen = st => {
  11. const { uid, gid } = st
  12. cache.set(path, { uid, gid })
  13. return { uid, gid }
  14. }
  15. const parent = dirname(path)
  16. const parentTrap = parent === path ? null : er => {
  17. return inferOwner(parent).then((owner) => {
  18. cache.set(path, owner)
  19. return owner
  20. })
  21. }
  22. return lstat(path).then(statThen, parentTrap)
  23. }
  24. const inferOwnerSync = path => {
  25. path = resolve(path)
  26. if (cache.has(path))
  27. return cache.get(path)
  28. const parent = dirname(path)
  29. // avoid obscuring call site by re-throwing
  30. // "catch" the error by returning from a finally,
  31. // only if we're not at the root, and the parent call works.
  32. let threw = true
  33. try {
  34. const st = fs.lstatSync(path)
  35. threw = false
  36. const { uid, gid } = st
  37. cache.set(path, { uid, gid })
  38. return { uid, gid }
  39. } finally {
  40. if (threw && parent !== path) {
  41. const owner = inferOwnerSync(parent)
  42. cache.set(path, owner)
  43. return owner // eslint-disable-line no-unsafe-finally
  44. }
  45. }
  46. }
  47. const inflight = new Map()
  48. module.exports = path => {
  49. path = resolve(path)
  50. if (inflight.has(path))
  51. return Promise.resolve(inflight.get(path))
  52. const p = inferOwner(path).then(owner => {
  53. inflight.delete(path)
  54. return owner
  55. })
  56. inflight.set(path, p)
  57. return p
  58. }
  59. module.exports.sync = inferOwnerSync
  60. module.exports.clearCache = () => {
  61. cache.clear()
  62. inflight.clear()
  63. }