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
			| 
								 
											4 years ago
										 
									 | 
							
								'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
							 |