chownr.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use strict'
  2. const fs = require('fs')
  3. const path = require('path')
  4. /* istanbul ignore next */
  5. const LCHOWN = fs.lchown ? 'lchown' : 'chown'
  6. /* istanbul ignore next */
  7. const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'
  8. // fs.readdir could only accept an options object as of node v6
  9. const nodeVersion = process.version
  10. let readdir = (path, options, cb) => fs.readdir(path, options, cb)
  11. let readdirSync = (path, options) => fs.readdirSync(path, options)
  12. /* istanbul ignore next */
  13. if (/^v4\./.test(nodeVersion))
  14. readdir = (path, options, cb) => fs.readdir(path, cb)
  15. const chownrKid = (p, child, uid, gid, cb) => {
  16. if (typeof child === 'string')
  17. return fs.lstat(path.resolve(p, child), (er, stats) => {
  18. if (er)
  19. return cb(er)
  20. stats.name = child
  21. chownrKid(p, stats, uid, gid, cb)
  22. })
  23. if (child.isDirectory()) {
  24. chownr(path.resolve(p, child.name), uid, gid, er => {
  25. if (er)
  26. return cb(er)
  27. fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
  28. })
  29. } else
  30. fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
  31. }
  32. const chownr = (p, uid, gid, cb) => {
  33. readdir(p, { withFileTypes: true }, (er, children) => {
  34. // any error other than ENOTDIR or ENOTSUP means it's not readable,
  35. // or doesn't exist. give up.
  36. if (er && er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP')
  37. return cb(er)
  38. if (er || !children.length) return fs[LCHOWN](p, uid, gid, cb)
  39. let len = children.length
  40. let errState = null
  41. const then = er => {
  42. if (errState) return
  43. if (er) return cb(errState = er)
  44. if (-- len === 0) return fs[LCHOWN](p, uid, gid, cb)
  45. }
  46. children.forEach(child => chownrKid(p, child, uid, gid, then))
  47. })
  48. }
  49. const chownrKidSync = (p, child, uid, gid) => {
  50. if (typeof child === 'string') {
  51. const stats = fs.lstatSync(path.resolve(p, child))
  52. stats.name = child
  53. child = stats
  54. }
  55. if (child.isDirectory())
  56. chownrSync(path.resolve(p, child.name), uid, gid)
  57. fs[LCHOWNSYNC](path.resolve(p, child.name), uid, gid)
  58. }
  59. const chownrSync = (p, uid, gid) => {
  60. let children
  61. try {
  62. children = readdirSync(p, { withFileTypes: true })
  63. } catch (er) {
  64. if (er && er.code === 'ENOTDIR' && er.code !== 'ENOTSUP')
  65. return fs[LCHOWNSYNC](p, uid, gid)
  66. throw er
  67. }
  68. if (children.length)
  69. children.forEach(child => chownrKidSync(p, child, uid, gid))
  70. return fs[LCHOWNSYNC](p, uid, gid)
  71. }
  72. module.exports = chownr
  73. chownr.sync = chownrSync