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
			| 
											3 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 |