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
 |