| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 | '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'/* 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 v6const nodeVersion = process.versionlet 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 = chownrchownr.sync = chownrSync
 |