You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							168 lines
						
					
					
						
							4.2 KiB
						
					
					
				
			
		
		
	
	
							168 lines
						
					
					
						
							4.2 KiB
						
					
					
				'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 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
 |