Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle EISDIR from lchown in node <10.6 #21

Merged
merged 3 commits into from Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 9 additions & 5 deletions .travis.yml
@@ -1,11 +1,15 @@
language: node_js
sudo: false

node_js:
- 6
- 8
- node
- 12
- 10
notifications:
email: false
- 8
- 6

cache:
directories:
- $HOME/.npm

notifications:
email: false
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
61 changes: 0 additions & 61 deletions test/symlink-sync.js

This file was deleted.

65 changes: 0 additions & 65 deletions test/symlink.js

This file was deleted.