143 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict'
 | |
| 
 | |
| const util = require('util')
 | |
| 
 | |
| const chownr = util.promisify(require('chownr'))
 | |
| const mkdirp = require('mkdirp')
 | |
| const inflight = require('promise-inflight')
 | |
| const inferOwner = require('infer-owner')
 | |
| 
 | |
| // Memoize getuid()/getgid() calls.
 | |
| // patch process.setuid/setgid to invalidate cached value on change
 | |
| const self = { uid: null, gid: null }
 | |
| const getSelf = () => {
 | |
|   if (typeof self.uid !== 'number') {
 | |
|     self.uid = process.getuid()
 | |
|     const setuid = process.setuid
 | |
|     process.setuid = (uid) => {
 | |
|       self.uid = null
 | |
|       process.setuid = setuid
 | |
|       return process.setuid(uid)
 | |
|     }
 | |
|   }
 | |
|   if (typeof self.gid !== 'number') {
 | |
|     self.gid = process.getgid()
 | |
|     const setgid = process.setgid
 | |
|     process.setgid = (gid) => {
 | |
|       self.gid = null
 | |
|       process.setgid = setgid
 | |
|       return process.setgid(gid)
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports.chownr = fixOwner
 | |
| 
 | |
| function fixOwner (cache, filepath) {
 | |
|   if (!process.getuid) {
 | |
|     // This platform doesn't need ownership fixing
 | |
|     return Promise.resolve()
 | |
|   }
 | |
| 
 | |
|   getSelf()
 | |
|   if (self.uid !== 0) {
 | |
|     // almost certainly can't chown anyway
 | |
|     return Promise.resolve()
 | |
|   }
 | |
| 
 | |
|   return Promise.resolve(inferOwner(cache)).then((owner) => {
 | |
|     const { uid, gid } = owner
 | |
| 
 | |
|     // No need to override if it's already what we used.
 | |
|     if (self.uid === uid && self.gid === gid)
 | |
|       return
 | |
| 
 | |
|     return inflight('fixOwner: fixing ownership on ' + filepath, () =>
 | |
|       chownr(
 | |
|         filepath,
 | |
|         typeof uid === 'number' ? uid : self.uid,
 | |
|         typeof gid === 'number' ? gid : self.gid
 | |
|       ).catch((err) => {
 | |
|         if (err.code === 'ENOENT')
 | |
|           return null
 | |
| 
 | |
|         throw err
 | |
|       })
 | |
|     )
 | |
|   })
 | |
| }
 | |
| 
 | |
| module.exports.chownr.sync = fixOwnerSync
 | |
| 
 | |
| function fixOwnerSync (cache, filepath) {
 | |
|   if (!process.getuid) {
 | |
|     // This platform doesn't need ownership fixing
 | |
|     return
 | |
|   }
 | |
|   const { uid, gid } = inferOwner.sync(cache)
 | |
|   getSelf()
 | |
|   if (self.uid !== 0) {
 | |
|     // almost certainly can't chown anyway
 | |
|     return
 | |
|   }
 | |
| 
 | |
|   if (self.uid === uid && self.gid === gid) {
 | |
|     // No need to override if it's already what we used.
 | |
|     return
 | |
|   }
 | |
|   try {
 | |
|     chownr.sync(
 | |
|       filepath,
 | |
|       typeof uid === 'number' ? uid : self.uid,
 | |
|       typeof gid === 'number' ? gid : self.gid
 | |
|     )
 | |
|   } catch (err) {
 | |
|     // only catch ENOENT, any other error is a problem.
 | |
|     if (err.code === 'ENOENT')
 | |
|       return null
 | |
| 
 | |
|     throw err
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports.mkdirfix = mkdirfix
 | |
| 
 | |
| function mkdirfix (cache, p, cb) {
 | |
|   // we have to infer the owner _before_ making the directory, even though
 | |
|   // we aren't going to use the results, since the cache itself might not
 | |
|   // exist yet.  If we mkdirp it, then our current uid/gid will be assumed
 | |
|   // to be correct if it creates the cache folder in the process.
 | |
|   return Promise.resolve(inferOwner(cache)).then(() => {
 | |
|     return mkdirp(p)
 | |
|       .then((made) => {
 | |
|         if (made)
 | |
|           return fixOwner(cache, made).then(() => made)
 | |
|       })
 | |
|       .catch((err) => {
 | |
|         if (err.code === 'EEXIST')
 | |
|           return fixOwner(cache, p).then(() => null)
 | |
| 
 | |
|         throw err
 | |
|       })
 | |
|   })
 | |
| }
 | |
| 
 | |
| module.exports.mkdirfix.sync = mkdirfixSync
 | |
| 
 | |
| function mkdirfixSync (cache, p) {
 | |
|   try {
 | |
|     inferOwner.sync(cache)
 | |
|     const made = mkdirp.sync(p)
 | |
|     if (made) {
 | |
|       fixOwnerSync(cache, made)
 | |
|       return made
 | |
|     }
 | |
|   } catch (err) {
 | |
|     if (err.code === 'EEXIST') {
 | |
|       fixOwnerSync(cache, p)
 | |
|       return null
 | |
|     } else
 | |
|       throw err
 | |
|   }
 | |
| }
 | 
