• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const BB = require('bluebird')
4
5const chownr = BB.promisify(require('chownr'))
6const mkdirp = BB.promisify(require('mkdirp'))
7const inflight = require('promise-inflight')
8const inferOwner = require('infer-owner')
9
10// Memoize getuid()/getgid() calls.
11// patch process.setuid/setgid to invalidate cached value on change
12const self = { uid: null, gid: null }
13const getSelf = () => {
14  if (typeof self.uid !== 'number') {
15    self.uid = process.getuid()
16    const setuid = process.setuid
17    process.setuid = (uid) => {
18      self.uid = null
19      process.setuid = setuid
20      return process.setuid(uid)
21    }
22  }
23  if (typeof self.gid !== 'number') {
24    self.gid = process.getgid()
25    const setgid = process.setgid
26    process.setgid = (gid) => {
27      self.gid = null
28      process.setgid = setgid
29      return process.setgid(gid)
30    }
31  }
32}
33
34module.exports.chownr = fixOwner
35function fixOwner (cache, filepath) {
36  if (!process.getuid) {
37    // This platform doesn't need ownership fixing
38    return BB.resolve()
39  }
40
41  getSelf()
42  if (self.uid !== 0) {
43    // almost certainly can't chown anyway
44    return BB.resolve()
45  }
46
47  return BB.resolve(inferOwner(cache)).then(owner => {
48    const { uid, gid } = owner
49
50    // No need to override if it's already what we used.
51    if (self.uid === uid && self.gid === gid) {
52      return
53    }
54
55    return inflight(
56      'fixOwner: fixing ownership on ' + filepath,
57      () => chownr(
58        filepath,
59        typeof uid === 'number' ? uid : self.uid,
60        typeof gid === 'number' ? gid : self.gid
61      ).catch({ code: 'ENOENT' }, () => null)
62    )
63  })
64}
65
66module.exports.chownr.sync = fixOwnerSync
67function fixOwnerSync (cache, filepath) {
68  if (!process.getuid) {
69    // This platform doesn't need ownership fixing
70    return
71  }
72  const { uid, gid } = inferOwner.sync(cache)
73  getSelf()
74  if (self.uid === uid && self.gid === gid) {
75    // No need to override if it's already what we used.
76    return
77  }
78  try {
79    chownr.sync(
80      filepath,
81      typeof uid === 'number' ? uid : self.uid,
82      typeof gid === 'number' ? gid : self.gid
83    )
84  } catch (err) {
85    // only catch ENOENT, any other error is a problem.
86    if (err.code === 'ENOENT') {
87      return null
88    }
89    throw err
90  }
91}
92
93module.exports.mkdirfix = mkdirfix
94function mkdirfix (cache, p, cb) {
95  // we have to infer the owner _before_ making the directory, even though
96  // we aren't going to use the results, since the cache itself might not
97  // exist yet.  If we mkdirp it, then our current uid/gid will be assumed
98  // to be correct if it creates the cache folder in the process.
99  return BB.resolve(inferOwner(cache)).then(() => {
100    return mkdirp(p).then(made => {
101      if (made) {
102        return fixOwner(cache, made).then(() => made)
103      }
104    }).catch({ code: 'EEXIST' }, () => {
105      // There's a race in mkdirp!
106      return fixOwner(cache, p).then(() => null)
107    })
108  })
109}
110
111module.exports.mkdirfix.sync = mkdirfixSync
112function mkdirfixSync (cache, p) {
113  try {
114    inferOwner.sync(cache)
115    const made = mkdirp.sync(p)
116    if (made) {
117      fixOwnerSync(cache, made)
118      return made
119    }
120  } catch (err) {
121    if (err.code === 'EEXIST') {
122      fixOwnerSync(cache, p)
123      return null
124    } else {
125      throw err
126    }
127  }
128}
129