cacache 12.0.3 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -1
- package/README.md +3 -29
- package/get.js +158 -139
- package/index.js +39 -1
- package/lib/content/path.js +7 -4
- package/lib/content/read.js +118 -55
- package/lib/content/rm.js +8 -9
- package/lib/content/write.js +120 -97
- package/lib/entry-index.js +135 -112
- package/lib/memoization.js +15 -10
- package/lib/util/disposer.js +30 -0
- package/lib/util/fix-owner.js +36 -19
- package/lib/util/hash-to-segments.js +1 -5
- package/lib/util/move-file.js +44 -27
- package/lib/util/tmp.js +8 -3
- package/lib/verify.js +162 -97
- package/ls.js +1 -1
- package/package.json +25 -23
- package/put.js +56 -48
- package/rm.js +5 -2
- package/README.es.md +0 -628
- package/en.js +0 -3
- package/es.js +0 -3
- package/lib/util/y.js +0 -25
- package/locales/en.js +0 -47
- package/locales/en.json +0 -7
- package/locales/es.js +0 -49
- package/locales/es.json +0 -6
package/lib/content/read.js
CHANGED
|
@@ -1,86 +1,109 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const util = require('util')
|
|
4
4
|
|
|
5
|
-
const contentPath = require('./path')
|
|
6
5
|
const figgyPudding = require('figgy-pudding')
|
|
7
6
|
const fs = require('graceful-fs')
|
|
8
|
-
const
|
|
9
|
-
const pipe = BB.promisify(require('mississippi').pipe)
|
|
7
|
+
const fsm = require('fs-minipass')
|
|
10
8
|
const ssri = require('ssri')
|
|
11
|
-
const
|
|
9
|
+
const contentPath = require('./path')
|
|
10
|
+
const Pipeline = require('minipass-pipeline')
|
|
12
11
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
const lstat = util.promisify(fs.lstat)
|
|
13
|
+
const readFile = util.promisify(fs.readFile)
|
|
15
14
|
|
|
16
15
|
const ReadOpts = figgyPudding({
|
|
17
16
|
size: {}
|
|
18
17
|
})
|
|
19
18
|
|
|
20
19
|
module.exports = read
|
|
20
|
+
|
|
21
|
+
const MAX_SINGLE_READ_SIZE = 64 * 1024 * 1024
|
|
21
22
|
function read (cache, integrity, opts) {
|
|
22
23
|
opts = ReadOpts(opts)
|
|
23
24
|
return withContentSri(cache, integrity, (cpath, sri) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// get size
|
|
26
|
+
return lstat(cpath).then(stat => ({ stat, cpath, sri }))
|
|
27
|
+
}).then(({ stat, cpath, sri }) => {
|
|
28
|
+
if (typeof opts.size === 'number' && stat.size !== opts.size) {
|
|
29
|
+
throw sizeError(opts.size, stat.size)
|
|
30
|
+
}
|
|
31
|
+
if (stat.size > MAX_SINGLE_READ_SIZE) {
|
|
32
|
+
return readPipeline(cpath, stat.size, sri, new Pipeline()).concat()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return readFile(cpath, null).then((data) => {
|
|
36
|
+
if (!ssri.checkData(data, sri)) {
|
|
30
37
|
throw integrityError(sri, cpath)
|
|
31
38
|
}
|
|
39
|
+
return data
|
|
32
40
|
})
|
|
33
41
|
})
|
|
34
42
|
}
|
|
35
43
|
|
|
44
|
+
const readPipeline = (cpath, size, sri, stream) => {
|
|
45
|
+
stream.push(
|
|
46
|
+
new fsm.ReadStream(cpath, {
|
|
47
|
+
size,
|
|
48
|
+
readSize: MAX_SINGLE_READ_SIZE
|
|
49
|
+
}),
|
|
50
|
+
ssri.integrityStream({
|
|
51
|
+
integrity: sri,
|
|
52
|
+
size
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
return stream
|
|
56
|
+
}
|
|
57
|
+
|
|
36
58
|
module.exports.sync = readSync
|
|
59
|
+
|
|
37
60
|
function readSync (cache, integrity, opts) {
|
|
38
61
|
opts = ReadOpts(opts)
|
|
39
62
|
return withContentSriSync(cache, integrity, (cpath, sri) => {
|
|
40
63
|
const data = fs.readFileSync(cpath)
|
|
41
64
|
if (typeof opts.size === 'number' && opts.size !== data.length) {
|
|
42
65
|
throw sizeError(opts.size, data.length)
|
|
43
|
-
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (ssri.checkData(data, sri)) {
|
|
44
69
|
return data
|
|
45
|
-
} else {
|
|
46
|
-
throw integrityError(sri, cpath)
|
|
47
70
|
}
|
|
71
|
+
|
|
72
|
+
throw integrityError(sri, cpath)
|
|
48
73
|
})
|
|
49
74
|
}
|
|
50
75
|
|
|
51
76
|
module.exports.stream = readStream
|
|
52
77
|
module.exports.readStream = readStream
|
|
78
|
+
|
|
53
79
|
function readStream (cache, integrity, opts) {
|
|
54
80
|
opts = ReadOpts(opts)
|
|
55
|
-
|
|
81
|
+
|
|
82
|
+
const stream = new Pipeline()
|
|
56
83
|
withContentSri(cache, integrity, (cpath, sri) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
}).catch(err => {
|
|
68
|
-
stream.emit('error', err)
|
|
69
|
-
})
|
|
84
|
+
// just lstat to ensure it exists
|
|
85
|
+
return lstat(cpath).then((stat) => ({ stat, cpath, sri }))
|
|
86
|
+
}).then(({ stat, cpath, sri }) => {
|
|
87
|
+
if (typeof opts.size === 'number' && opts.size !== stat.size) {
|
|
88
|
+
return stream.emit('error', sizeError(opts.size, stat.size))
|
|
89
|
+
}
|
|
90
|
+
readPipeline(cpath, stat.size, sri, stream)
|
|
91
|
+
}, er => stream.emit('error', er))
|
|
92
|
+
|
|
70
93
|
return stream
|
|
71
94
|
}
|
|
72
95
|
|
|
73
|
-
let
|
|
96
|
+
let copyFile
|
|
74
97
|
if (fs.copyFile) {
|
|
75
98
|
module.exports.copy = copy
|
|
76
99
|
module.exports.copy.sync = copySync
|
|
77
|
-
|
|
100
|
+
copyFile = util.promisify(fs.copyFile)
|
|
78
101
|
}
|
|
79
102
|
|
|
80
103
|
function copy (cache, integrity, dest, opts) {
|
|
81
104
|
opts = ReadOpts(opts)
|
|
82
105
|
return withContentSri(cache, integrity, (cpath, sri) => {
|
|
83
|
-
return
|
|
106
|
+
return copyFile(cpath, dest)
|
|
84
107
|
})
|
|
85
108
|
}
|
|
86
109
|
|
|
@@ -92,13 +115,19 @@ function copySync (cache, integrity, dest, opts) {
|
|
|
92
115
|
}
|
|
93
116
|
|
|
94
117
|
module.exports.hasContent = hasContent
|
|
118
|
+
|
|
95
119
|
function hasContent (cache, integrity) {
|
|
96
|
-
if (!integrity) {
|
|
120
|
+
if (!integrity) {
|
|
121
|
+
return Promise.resolve(false)
|
|
122
|
+
}
|
|
97
123
|
return withContentSri(cache, integrity, (cpath, sri) => {
|
|
98
|
-
return
|
|
99
|
-
}).catch(err => {
|
|
100
|
-
if (err.code === 'ENOENT') {
|
|
124
|
+
return lstat(cpath).then((stat) => ({ size: stat.size, sri, stat }))
|
|
125
|
+
}).catch((err) => {
|
|
126
|
+
if (err.code === 'ENOENT') {
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
101
129
|
if (err.code === 'EPERM') {
|
|
130
|
+
/* istanbul ignore else */
|
|
102
131
|
if (process.platform !== 'win32') {
|
|
103
132
|
throw err
|
|
104
133
|
} else {
|
|
@@ -109,15 +138,21 @@ function hasContent (cache, integrity) {
|
|
|
109
138
|
}
|
|
110
139
|
|
|
111
140
|
module.exports.hasContent.sync = hasContentSync
|
|
141
|
+
|
|
112
142
|
function hasContentSync (cache, integrity) {
|
|
113
|
-
if (!integrity) {
|
|
143
|
+
if (!integrity) {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
114
146
|
return withContentSriSync(cache, integrity, (cpath, sri) => {
|
|
115
147
|
try {
|
|
116
148
|
const stat = fs.lstatSync(cpath)
|
|
117
149
|
return { size: stat.size, sri, stat }
|
|
118
150
|
} catch (err) {
|
|
119
|
-
if (err.code === 'ENOENT') {
|
|
151
|
+
if (err.code === 'ENOENT') {
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
120
154
|
if (err.code === 'EPERM') {
|
|
155
|
+
/* istanbul ignore else */
|
|
121
156
|
if (process.platform !== 'win32') {
|
|
122
157
|
throw err
|
|
123
158
|
} else {
|
|
@@ -129,30 +164,58 @@ function hasContentSync (cache, integrity) {
|
|
|
129
164
|
}
|
|
130
165
|
|
|
131
166
|
function withContentSri (cache, integrity, fn) {
|
|
132
|
-
|
|
167
|
+
const tryFn = () => {
|
|
133
168
|
const sri = ssri.parse(integrity)
|
|
134
169
|
// If `integrity` has multiple entries, pick the first digest
|
|
135
170
|
// with available local data.
|
|
136
171
|
const algo = sri.pickAlgorithm()
|
|
137
172
|
const digests = sri[algo]
|
|
173
|
+
|
|
138
174
|
if (digests.length <= 1) {
|
|
139
175
|
const cpath = contentPath(cache, digests[0])
|
|
140
176
|
return fn(cpath, digests[0])
|
|
141
177
|
} else {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
178
|
+
// Can't use race here because a generic error can happen before a ENOENT error, and can happen before a valid result
|
|
179
|
+
return Promise
|
|
180
|
+
.all(digests.map((meta) => {
|
|
181
|
+
return withContentSri(cache, meta, fn)
|
|
182
|
+
.catch((err) => {
|
|
183
|
+
if (err.code === 'ENOENT') {
|
|
184
|
+
return Object.assign(
|
|
185
|
+
new Error('No matching content found for ' + sri.toString()),
|
|
186
|
+
{ code: 'ENOENT' }
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
return err
|
|
190
|
+
})
|
|
191
|
+
}))
|
|
192
|
+
.then((results) => {
|
|
193
|
+
// Return the first non error if it is found
|
|
194
|
+
const result = results.find((r) => !(r instanceof Error))
|
|
195
|
+
if (result) {
|
|
196
|
+
return result
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Throw the No matching content found error
|
|
200
|
+
const enoentError = results.find((r) => r.code === 'ENOENT')
|
|
201
|
+
if (enoentError) {
|
|
202
|
+
throw enoentError
|
|
153
203
|
}
|
|
204
|
+
|
|
205
|
+
// Throw generic error
|
|
206
|
+
throw results.find((r) => r instanceof Error)
|
|
154
207
|
})
|
|
155
208
|
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
try {
|
|
213
|
+
tryFn()
|
|
214
|
+
.then(resolve)
|
|
215
|
+
.catch(reject)
|
|
216
|
+
} catch (err) {
|
|
217
|
+
reject(err)
|
|
218
|
+
}
|
|
156
219
|
})
|
|
157
220
|
}
|
|
158
221
|
|
|
@@ -167,19 +230,19 @@ function withContentSriSync (cache, integrity, fn) {
|
|
|
167
230
|
return fn(cpath, digests[0])
|
|
168
231
|
} else {
|
|
169
232
|
let lastErr = null
|
|
170
|
-
for (const meta of
|
|
233
|
+
for (const meta of digests) {
|
|
171
234
|
try {
|
|
172
235
|
return withContentSriSync(cache, meta, fn)
|
|
173
236
|
} catch (err) {
|
|
174
237
|
lastErr = err
|
|
175
238
|
}
|
|
176
239
|
}
|
|
177
|
-
|
|
240
|
+
throw lastErr
|
|
178
241
|
}
|
|
179
242
|
}
|
|
180
243
|
|
|
181
244
|
function sizeError (expected, found) {
|
|
182
|
-
|
|
245
|
+
const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
|
|
183
246
|
err.expected = expected
|
|
184
247
|
err.found = found
|
|
185
248
|
err.code = 'EBADSIZE'
|
|
@@ -187,7 +250,7 @@ function sizeError (expected, found) {
|
|
|
187
250
|
}
|
|
188
251
|
|
|
189
252
|
function integrityError (sri, path) {
|
|
190
|
-
|
|
253
|
+
const err = new Error(`Integrity verification failed for ${sri} (${path})`)
|
|
191
254
|
err.code = 'EINTEGRITY'
|
|
192
255
|
err.sri = sri
|
|
193
256
|
err.path = path
|
package/lib/content/rm.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const util = require('util')
|
|
4
4
|
|
|
5
5
|
const contentPath = require('./path')
|
|
6
|
-
const hasContent = require('./read')
|
|
7
|
-
const rimraf =
|
|
6
|
+
const { hasContent } = require('./read')
|
|
7
|
+
const rimraf = util.promisify(require('rimraf'))
|
|
8
8
|
|
|
9
9
|
module.exports = rm
|
|
10
|
+
|
|
10
11
|
function rm (cache, integrity) {
|
|
11
|
-
return hasContent(cache, integrity).then(content => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return rimraf(contentPath(cache, sri)).then(() => true)
|
|
16
|
-
}
|
|
12
|
+
return hasContent(cache, integrity).then((content) => {
|
|
13
|
+
// ~pretty~ sure we can't end up with a content lacking sri, but be safe
|
|
14
|
+
if (content && content.sri) {
|
|
15
|
+
return rimraf(contentPath(cache, content.sri)).then(() => true)
|
|
17
16
|
} else {
|
|
18
17
|
return false
|
|
19
18
|
}
|
package/lib/content/write.js
CHANGED
|
@@ -1,152 +1,175 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const util = require('util')
|
|
4
4
|
|
|
5
5
|
const contentPath = require('./path')
|
|
6
6
|
const fixOwner = require('../util/fix-owner')
|
|
7
7
|
const fs = require('graceful-fs')
|
|
8
8
|
const moveFile = require('../util/move-file')
|
|
9
|
-
const
|
|
9
|
+
const Minipass = require('minipass')
|
|
10
|
+
const Pipeline = require('minipass-pipeline')
|
|
11
|
+
const Flush = require('minipass-flush')
|
|
10
12
|
const path = require('path')
|
|
11
|
-
const
|
|
12
|
-
const rimraf = BB.promisify(require('rimraf'))
|
|
13
|
+
const rimraf = util.promisify(require('rimraf'))
|
|
13
14
|
const ssri = require('ssri')
|
|
14
|
-
const to = require('mississippi').to
|
|
15
15
|
const uniqueFilename = require('unique-filename')
|
|
16
|
-
const
|
|
16
|
+
const { disposer } = require('./../util/disposer')
|
|
17
|
+
const fsm = require('fs-minipass')
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const writeFile = util.promisify(fs.writeFile)
|
|
19
20
|
|
|
20
21
|
module.exports = write
|
|
22
|
+
|
|
21
23
|
function write (cache, data, opts) {
|
|
22
24
|
opts = opts || {}
|
|
23
25
|
if (opts.algorithms && opts.algorithms.length > 1) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
Y`opts.algorithms only supports a single algorithm for now`
|
|
26
|
-
)
|
|
26
|
+
throw new Error('opts.algorithms only supports a single algorithm for now')
|
|
27
27
|
}
|
|
28
28
|
if (typeof opts.size === 'number' && data.length !== opts.size) {
|
|
29
|
-
return
|
|
29
|
+
return Promise.reject(sizeError(opts.size, data.length))
|
|
30
30
|
}
|
|
31
31
|
const sri = ssri.fromData(data, {
|
|
32
32
|
algorithms: opts.algorithms
|
|
33
33
|
})
|
|
34
34
|
if (opts.integrity && !ssri.checkData(data, opts.integrity, opts)) {
|
|
35
|
-
return
|
|
35
|
+
return Promise.reject(checksumError(opts.integrity, sri))
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
)
|
|
43
|
-
|
|
37
|
+
|
|
38
|
+
return disposer(makeTmp(cache, opts), makeTmpDisposer,
|
|
39
|
+
(tmp) => {
|
|
40
|
+
return writeFile(tmp.target, data, { flag: 'wx' })
|
|
41
|
+
.then(() => moveToDestination(tmp, cache, sri, opts))
|
|
42
|
+
})
|
|
43
|
+
.then(() => ({ integrity: sri, size: data.length }))
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
module.exports.stream = writeStream
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
|
|
48
|
+
// writes proxied to the 'inputStream' that is passed to the Promise
|
|
49
|
+
// 'end' is deferred until content is handled.
|
|
50
|
+
class CacacheWriteStream extends Flush {
|
|
51
|
+
constructor (cache, opts) {
|
|
52
|
+
super()
|
|
53
|
+
this.opts = opts
|
|
54
|
+
this.cache = cache
|
|
55
|
+
this.inputStream = new Minipass()
|
|
56
|
+
this.inputStream.on('error', er => this.emit('error', er))
|
|
57
|
+
this.inputStream.on('drain', () => this.emit('drain'))
|
|
58
|
+
this.handleContentP = null
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
write (chunk, encoding, cb) {
|
|
62
|
+
if (!this.handleContentP) {
|
|
63
|
+
this.handleContentP = handleContent(
|
|
64
|
+
this.inputStream,
|
|
65
|
+
this.cache,
|
|
66
|
+
this.opts
|
|
67
|
+
)
|
|
59
68
|
}
|
|
60
|
-
inputStream.write(
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
return this.inputStream.write(chunk, encoding, cb)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
flush (cb) {
|
|
73
|
+
this.inputStream.end(() => {
|
|
74
|
+
if (!this.handleContentP) {
|
|
75
|
+
const e = new Error('Cache input stream was empty')
|
|
65
76
|
e.code = 'ENODATA'
|
|
66
|
-
|
|
77
|
+
// empty streams are probably emitting end right away.
|
|
78
|
+
// defer this one tick by rejecting a promise on it.
|
|
79
|
+
return Promise.reject(e).catch(cb)
|
|
67
80
|
}
|
|
68
|
-
|
|
69
|
-
res
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
this.handleContentP.then(
|
|
82
|
+
(res) => {
|
|
83
|
+
res.integrity && this.emit('integrity', res.integrity)
|
|
84
|
+
res.size !== null && this.emit('size', res.size)
|
|
85
|
+
cb()
|
|
86
|
+
},
|
|
87
|
+
(er) => cb(er)
|
|
88
|
+
)
|
|
75
89
|
})
|
|
76
|
-
}
|
|
77
|
-
ret.once('error', e => {
|
|
78
|
-
inputErr = e
|
|
79
|
-
})
|
|
80
|
-
return ret
|
|
90
|
+
}
|
|
81
91
|
}
|
|
82
92
|
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
function writeStream (cache, opts) {
|
|
94
|
+
opts = opts || {}
|
|
95
|
+
return new CacacheWriteStream(cache, opts)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleContent (inputStream, cache, opts) {
|
|
99
|
+
return disposer(makeTmp(cache, opts), makeTmpDisposer, (tmp) => {
|
|
100
|
+
return pipeToTmp(inputStream, cache, tmp.target, opts)
|
|
101
|
+
.then((res) => {
|
|
102
|
+
return moveToDestination(
|
|
103
|
+
tmp,
|
|
104
|
+
cache,
|
|
105
|
+
res.integrity,
|
|
106
|
+
opts
|
|
107
|
+
).then(() => res)
|
|
108
|
+
})
|
|
93
109
|
})
|
|
94
110
|
}
|
|
95
111
|
|
|
96
|
-
function pipeToTmp (inputStream, cache, tmpTarget, opts
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const outStream = fs.createWriteStream(tmpTarget, {
|
|
110
|
-
flags: 'wx'
|
|
111
|
-
})
|
|
112
|
-
errCheck()
|
|
113
|
-
return pipe(inputStream, hashStream, outStream).then(() => {
|
|
114
|
-
return { integrity, size }
|
|
115
|
-
}).catch(err => {
|
|
116
|
-
return rimraf(tmpTarget).then(() => { throw err })
|
|
117
|
-
})
|
|
112
|
+
function pipeToTmp (inputStream, cache, tmpTarget, opts) {
|
|
113
|
+
let integrity
|
|
114
|
+
let size
|
|
115
|
+
const hashStream = ssri.integrityStream({
|
|
116
|
+
integrity: opts.integrity,
|
|
117
|
+
algorithms: opts.algorithms,
|
|
118
|
+
size: opts.size
|
|
119
|
+
})
|
|
120
|
+
hashStream.on('integrity', i => { integrity = i })
|
|
121
|
+
hashStream.on('size', s => { size = s })
|
|
122
|
+
|
|
123
|
+
const outStream = new fsm.WriteStream(tmpTarget, {
|
|
124
|
+
flags: 'wx'
|
|
118
125
|
})
|
|
126
|
+
|
|
127
|
+
// NB: this can throw if the hashStream has a problem with
|
|
128
|
+
// it, and the data is fully written. but pipeToTmp is only
|
|
129
|
+
// called in promisory contexts where that is handled.
|
|
130
|
+
const pipeline = new Pipeline(
|
|
131
|
+
inputStream,
|
|
132
|
+
hashStream,
|
|
133
|
+
outStream
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return pipeline.promise()
|
|
137
|
+
.then(() => ({ integrity, size }))
|
|
138
|
+
.catch(er => rimraf(tmpTarget).then(() => { throw er }))
|
|
119
139
|
}
|
|
120
140
|
|
|
121
141
|
function makeTmp (cache, opts) {
|
|
122
142
|
const tmpTarget = uniqueFilename(path.join(cache, 'tmp'), opts.tmpPrefix)
|
|
123
|
-
return fixOwner.mkdirfix(
|
|
124
|
-
cache, path.dirname(tmpTarget)
|
|
125
|
-
).then(() => ({
|
|
143
|
+
return fixOwner.mkdirfix(cache, path.dirname(tmpTarget)).then(() => ({
|
|
126
144
|
target: tmpTarget,
|
|
127
145
|
moved: false
|
|
128
|
-
}))
|
|
146
|
+
}))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function makeTmpDisposer (tmp) {
|
|
150
|
+
if (tmp.moved) {
|
|
151
|
+
return Promise.resolve()
|
|
152
|
+
}
|
|
153
|
+
return rimraf(tmp.target)
|
|
129
154
|
}
|
|
130
155
|
|
|
131
|
-
function moveToDestination (tmp, cache, sri, opts
|
|
132
|
-
errCheck && errCheck()
|
|
156
|
+
function moveToDestination (tmp, cache, sri, opts) {
|
|
133
157
|
const destination = contentPath(cache, sri)
|
|
134
158
|
const destDir = path.dirname(destination)
|
|
135
159
|
|
|
136
|
-
return fixOwner
|
|
137
|
-
cache, destDir
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
})
|
|
160
|
+
return fixOwner
|
|
161
|
+
.mkdirfix(cache, destDir)
|
|
162
|
+
.then(() => {
|
|
163
|
+
return moveFile(tmp.target, destination)
|
|
164
|
+
})
|
|
165
|
+
.then(() => {
|
|
166
|
+
tmp.moved = true
|
|
167
|
+
return fixOwner.chownr(cache, destination)
|
|
168
|
+
})
|
|
146
169
|
}
|
|
147
170
|
|
|
148
171
|
function sizeError (expected, found) {
|
|
149
|
-
|
|
172
|
+
const err = new Error(`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
|
|
150
173
|
err.expected = expected
|
|
151
174
|
err.found = found
|
|
152
175
|
err.code = 'EBADSIZE'
|
|
@@ -154,7 +177,7 @@ function sizeError (expected, found) {
|
|
|
154
177
|
}
|
|
155
178
|
|
|
156
179
|
function checksumError (expected, found) {
|
|
157
|
-
|
|
180
|
+
const err = new Error(`Integrity check failed:
|
|
158
181
|
Wanted: ${expected}
|
|
159
182
|
Found: ${found}`)
|
|
160
183
|
err.code = 'EINTEGRITY'
|