Projekt

Obecné

Profil

Stáhnout (4.17 KB) Statistiky
| Větev: | Revize:
1
'use strict'
2
const fs = require('fs')
3
const path = require('path')
4

    
5
/* istanbul ignore next */
6
const LCHOWN = fs.lchown ? 'lchown' : 'chown'
7
/* istanbul ignore next */
8
const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'
9

    
10
/* istanbul ignore next */
11
const needEISDIRHandled = fs.lchown &&
12
  !process.version.match(/v1[1-9]+\./) &&
13
  !process.version.match(/v10\.[6-9]/)
14

    
15
const lchownSync = (path, uid, gid) => {
16
  try {
17
    return fs[LCHOWNSYNC](path, uid, gid)
18
  } catch (er) {
19
    if (er.code !== 'ENOENT')
20
      throw er
21
  }
22
}
23

    
24
/* istanbul ignore next */
25
const chownSync = (path, uid, gid) => {
26
  try {
27
    return fs.chownSync(path, uid, gid)
28
  } catch (er) {
29
    if (er.code !== 'ENOENT')
30
      throw er
31
  }
32
}
33

    
34
/* istanbul ignore next */
35
const handleEISDIR =
36
  needEISDIRHandled ? (path, uid, gid, cb) => er => {
37
    // Node prior to v10 had a very questionable implementation of
38
    // fs.lchown, which would always try to call fs.open on a directory
39
    // Fall back to fs.chown in those cases.
40
    if (!er || er.code !== 'EISDIR')
41
      cb(er)
42
    else
43
      fs.chown(path, uid, gid, cb)
44
  }
45
  : (_, __, ___, cb) => cb
46

    
47
/* istanbul ignore next */
48
const handleEISDirSync =
49
  needEISDIRHandled ? (path, uid, gid) => {
50
    try {
51
      return lchownSync(path, uid, gid)
52
    } catch (er) {
53
      if (er.code !== 'EISDIR')
54
        throw er
55
      chownSync(path, uid, gid)
56
    }
57
  }
58
  : (path, uid, gid) => lchownSync(path, uid, gid)
59

    
60
// fs.readdir could only accept an options object as of node v6
61
const nodeVersion = process.version
62
let readdir = (path, options, cb) => fs.readdir(path, options, cb)
63
let readdirSync = (path, options) => fs.readdirSync(path, options)
64
/* istanbul ignore next */
65
if (/^v4\./.test(nodeVersion))
66
  readdir = (path, options, cb) => fs.readdir(path, cb)
67

    
68
const chown = (cpath, uid, gid, cb) => {
69
  fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, er => {
70
    // Skip ENOENT error
71
    cb(er && er.code !== 'ENOENT' ? er : null)
72
  }))
73
}
74

    
75
const chownrKid = (p, child, uid, gid, cb) => {
76
  if (typeof child === 'string')
77
    return fs.lstat(path.resolve(p, child), (er, stats) => {
78
      // Skip ENOENT error
79
      if (er)
80
        return cb(er.code !== 'ENOENT' ? er : null)
81
      stats.name = child
82
      chownrKid(p, stats, uid, gid, cb)
83
    })
84

    
85
  if (child.isDirectory()) {
86
    chownr(path.resolve(p, child.name), uid, gid, er => {
87
      if (er)
88
        return cb(er)
89
      const cpath = path.resolve(p, child.name)
90
      chown(cpath, uid, gid, cb)
91
    })
92
  } else {
93
    const cpath = path.resolve(p, child.name)
94
    chown(cpath, uid, gid, cb)
95
  }
96
}
97

    
98

    
99
const chownr = (p, uid, gid, cb) => {
100
  readdir(p, { withFileTypes: true }, (er, children) => {
101
    // any error other than ENOTDIR or ENOTSUP means it's not readable,
102
    // or doesn't exist.  give up.
103
    if (er) {
104
      if (er.code === 'ENOENT')
105
        return cb()
106
      else if (er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP')
107
        return cb(er)
108
    }
109
    if (er || !children.length)
110
      return chown(p, uid, gid, cb)
111

    
112
    let len = children.length
113
    let errState = null
114
    const then = er => {
115
      if (errState)
116
        return
117
      if (er)
118
        return cb(errState = er)
119
      if (-- len === 0)
120
        return chown(p, uid, gid, cb)
121
    }
122

    
123
    children.forEach(child => chownrKid(p, child, uid, gid, then))
124
  })
125
}
126

    
127
const chownrKidSync = (p, child, uid, gid) => {
128
  if (typeof child === 'string') {
129
    try {
130
      const stats = fs.lstatSync(path.resolve(p, child))
131
      stats.name = child
132
      child = stats
133
    } catch (er) {
134
      if (er.code === 'ENOENT')
135
        return
136
      else
137
        throw er
138
    }
139
  }
140

    
141
  if (child.isDirectory())
142
    chownrSync(path.resolve(p, child.name), uid, gid)
143

    
144
  handleEISDirSync(path.resolve(p, child.name), uid, gid)
145
}
146

    
147
const chownrSync = (p, uid, gid) => {
148
  let children
149
  try {
150
    children = readdirSync(p, { withFileTypes: true })
151
  } catch (er) {
152
    if (er.code === 'ENOENT')
153
      return
154
    else if (er.code === 'ENOTDIR' || er.code === 'ENOTSUP')
155
      return handleEISDirSync(p, uid, gid)
156
    else
157
      throw er
158
  }
159

    
160
  if (children && children.length)
161
    children.forEach(child => chownrKidSync(p, child, uid, gid))
162

    
163
  return handleEISDirSync(p, uid, gid)
164
}
165

    
166
module.exports = chownr
167
chownr.sync = chownrSync
(3-3/4)