Skip to content

Commit

Permalink
Handle EISDIR from lchown in node <10.6
Browse files Browse the repository at this point in the history
Fix #20
  • Loading branch information
isaacs committed Jul 3, 2019
1 parent 7a5c3d5 commit b06e7ef
Showing 1 changed file with 47 additions and 10 deletions.
57 changes: 47 additions & 10 deletions chownr.js
Expand Up @@ -7,6 +7,36 @@ const LCHOWN = fs.lchown ? 'lchown' : 'chown'
/* istanbul ignore next */
const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'

const needEISDIRHandled = fs.lchown &&
!process.version.match(/v1[1-9]+\./) &&
!process.version.match(/v10\.[6-9]/)

/* istanbul ignore next */
const handleEISDIR =
needEISDIRHandled ? (path, uid, gid, cb) => er => {
// Node prior to v10 had a very questionable implementation of
// fs.lchown, which would always try to call fs.open on a directory
// Fall back to fs.chown in those cases.
if (!er || er.code !== 'EISDIR')
cb(er)
else
fs.chown(path, uid, gid, cb)
}
: (_, __, ___, cb) => cb

/* istanbul ignore next */
const handleEISDirSync =
needEISDIRHandled ? (path, uid, gid) => {
try {
return fs[LCHOWNSYNC](path, uid, gid)
} catch (er) {
if (er.code !== 'EISDIR')
throw er
fs.chownSync(path, uid, gid)
}
}
: fs[LCHOWNSYNC]

// 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)
Expand All @@ -28,10 +58,13 @@ const chownrKid = (p, child, uid, gid, cb) => {
chownr(path.resolve(p, child.name), uid, gid, er => {
if (er)
return cb(er)
fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
const cpath = path.resolve(p, child.name)
fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, cb))
})
} else
fs[LCHOWN](path.resolve(p, child.name), uid, gid, cb)
} else {
const cpath = path.resolve(p, child.name)
fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, cb))
}
}


Expand All @@ -41,14 +74,18 @@ const chownr = (p, uid, gid, cb) => {
// 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)
if (er || !children.length)
return fs[LCHOWN](p, uid, gid, handleEISDIR(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)
if (errState)
return
if (er)
return cb(errState = er)
if (-- len === 0)
return fs[LCHOWN](p, uid, gid, handleEISDIR(p, uid, gid, cb))
}

children.forEach(child => chownrKid(p, child, uid, gid, then))
Expand All @@ -65,7 +102,7 @@ const chownrKidSync = (p, child, uid, gid) => {
if (child.isDirectory())
chownrSync(path.resolve(p, child.name), uid, gid)

fs[LCHOWNSYNC](path.resolve(p, child.name), uid, gid)
handleEISDirSync(path.resolve(p, child.name), uid, gid)
}

const chownrSync = (p, uid, gid) => {
Expand All @@ -74,14 +111,14 @@ const chownrSync = (p, uid, gid) => {
children = readdirSync(p, { withFileTypes: true })
} catch (er) {
if (er && er.code === 'ENOTDIR' && er.code !== 'ENOTSUP')
return fs[LCHOWNSYNC](p, uid, gid)
return handleEISDirSync(p, uid, gid)
throw er
}

if (children.length)
children.forEach(child => chownrKidSync(p, child, uid, gid))

return fs[LCHOWNSYNC](p, uid, gid)
return handleEISDirSync(p, uid, gid)
}

module.exports = chownr
Expand Down

0 comments on commit b06e7ef

Please sign in to comment.