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