Spaces:
Runtime error
Runtime error
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' | |
/* istanbul ignore next */ | |
const needEISDIRHandled = fs.lchown && | |
!process.version.match(/v1[1-9]+\./) && | |
!process.version.match(/v10\.[6-9]/) | |
const lchownSync = (path, uid, gid) => { | |
try { | |
return fs[LCHOWNSYNC](path, uid, gid) | |
} catch (er) { | |
if (er.code !== 'ENOENT') | |
throw er | |
} | |
} | |
/* istanbul ignore next */ | |
const chownSync = (path, uid, gid) => { | |
try { | |
return fs.chownSync(path, uid, gid) | |
} catch (er) { | |
if (er.code !== 'ENOENT') | |
throw er | |
} | |
} | |
/* 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 lchownSync(path, uid, gid) | |
} catch (er) { | |
if (er.code !== 'EISDIR') | |
throw er | |
chownSync(path, uid, gid) | |
} | |
} | |
: (path, uid, gid) => lchownSync(path, uid, gid) | |
// 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 chown = (cpath, uid, gid, cb) => { | |
fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => { | |
// Skip ENOENT error | |
cb(er && er.code !== 'ENOENT' ? er : null) | |
})) | |
} | |
const chownrKid = (p, child, uid, gid, cb) => { | |
if (typeof child === 'string') | |
return fs.lstat(path.resolve(p, child), (er, stats) => { | |
// Skip ENOENT error | |
if (er) | |
return cb(er.code !== 'ENOENT' ? er : null) | |
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) | |
const cpath = path.resolve(p, child.name) | |
chown(cpath, uid, gid, cb) | |
}) | |
} else { | |
const cpath = path.resolve(p, child.name) | |
chown(cpath, 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) { | |
if (er.code === 'ENOENT') | |
return cb() | |
else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP') | |
return cb(er) | |
} | |
if (er || !children.length) | |
return chown(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 chown(p, uid, gid, cb) | |
} | |
children.forEach(child => chownrKid(p, child, uid, gid, then)) | |
}) | |
} | |
const chownrKidSync = (p, child, uid, gid) => { | |
if (typeof child === 'string') { | |
try { | |
const stats = fs.lstatSync(path.resolve(p, child)) | |
stats.name = child | |
child = stats | |
} catch (er) { | |
if (er.code === 'ENOENT') | |
return | |
else | |
throw er | |
} | |
} | |
if (child.isDirectory()) | |
chownrSync(path.resolve(p, child.name), uid, gid) | |
handleEISDirSync(path.resolve(p, child.name), uid, gid) | |
} | |
const chownrSync = (p, uid, gid) => { | |
let children | |
try { | |
children = readdirSync(p, { withFileTypes: true }) | |
} catch (er) { | |
if (er.code === 'ENOENT') | |
return | |
else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP') | |
return handleEISDirSync(p, uid, gid) | |
else | |
throw er | |
} | |
if (children && children.length) | |
children.forEach(child => chownrKidSync(p, child, uid, gid)) | |
return handleEISDirSync(p, uid, gid) | |
} | |
module.exports = chownr | |
chownr.sync = chownrSync | |