89 lines
2.4 KiB
JavaScript
89 lines
2.4 KiB
JavaScript
'use strict'
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
/* istanbul ignore next */
|
|
const LCHOWN = fs.lchown ? 'lchown' : 'chown'
|
|
/* istanbul ignore next */
|
|
const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'
|
|
|
|
// fs.readdir could only accept an options object as of node v6
|
|
const nodeVersion = process.version
|
|
let readdir = (path, options, cb) => fs.readdir(path, options, cb)
|
|
let readdirSync = (path, options) => fs.readdirSync(path, options)
|
|
/* istanbul ignore next */
|
|
if (/^v4\./.test(nodeVersion))
|
|
readdir = (path, options, cb) => fs.readdir(path, cb)
|
|
|
|
const chownrKid = (p, child, uid, gid, cb) => {
|
|
if (typeof child === 'string')
|
|
return fs.lstat(path.resolve(p, child), (er, stats) => {
|
|
if (er)
|
|
return cb(er)
|
|
stats.name = child
|
|
chownrKid(p, stats, uid, gid, cb)
|
|
})
|
|
|
|
if (child.isDirectory()) {
|
|
chownr(path.resolve(p, child.name), uid, gid, er => {
|
|
if (er)
|
|
return cb(er)
|
|
fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
|
|
})
|
|
} else
|
|
fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
|
|
}
|
|
|
|
|
|
const chownr = (p, uid, gid, cb) => {
|
|
readdir(p, { withFileTypes: true }, (er, children) => {
|
|
// any error other than ENOTDIR or ENOTSUP means it's not readable,
|
|
// or doesn't exist. give up.
|
|
if (er && er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP')
|
|
return cb(er)
|
|
if (er || !children.length) return fs[LCHOWN](p, uid, gid, cb)
|
|
|
|
let len = children.length
|
|
let errState = null
|
|
const then = er => {
|
|
if (errState) return
|
|
if (er) return cb(errState = er)
|
|
if (-- len === 0) return fs[LCHOWN](p, uid, gid, cb)
|
|
}
|
|
|
|
children.forEach(child => chownrKid(p, child, uid, gid, then))
|
|
})
|
|
}
|
|
|
|
const chownrKidSync = (p, child, uid, gid) => {
|
|
if (typeof child === 'string') {
|
|
const stats = fs.lstatSync(path.resolve(p, child))
|
|
stats.name = child
|
|
child = stats
|
|
}
|
|
|
|
if (child.isDirectory())
|
|
chownrSync(path.resolve(p, child.name), uid, gid)
|
|
|
|
fs[LCHOWNSYNC](path.resolve(p, child.name), uid, gid)
|
|
}
|
|
|
|
const chownrSync = (p, uid, gid) => {
|
|
let children
|
|
try {
|
|
children = readdirSync(p, { withFileTypes: true })
|
|
} catch (er) {
|
|
if (er && er.code === 'ENOTDIR' && er.code !== 'ENOTSUP')
|
|
return fs[LCHOWNSYNC](p, uid, gid)
|
|
throw er
|
|
}
|
|
|
|
if (children.length)
|
|
children.forEach(child => chownrKidSync(p, child, uid, gid))
|
|
|
|
return fs[LCHOWNSYNC](p, uid, gid)
|
|
}
|
|
|
|
module.exports = chownr
|
|
chownr.sync = chownrSync
|