summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/cacache/lib/content/read.js
blob: 5c1a6f2f290b451b57f534eb947f3432ba8530c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
'use strict'

const BB = require('bluebird')

const contentPath = require('./path')
const figgyPudding = require('figgy-pudding')
const fs = require('graceful-fs')
const PassThrough = require('stream').PassThrough
const pipe = BB.promisify(require('mississippi').pipe)
const ssri = require('ssri')
const Y = require('../util/y.js')

BB.promisifyAll(fs)

const ReadOpts = figgyPudding({
  size: {}
})

module.exports = read
function read (cache, integrity, opts) {
  opts = ReadOpts(opts)
  return pickContentSri(cache, integrity).then(content => {
    const sri = content.sri
    const cpath = contentPath(cache, sri)
    return fs.readFileAsync(cpath, null).then(data => {
      if (typeof opts.size === 'number' && opts.size !== data.length) {
        throw sizeError(opts.size, data.length)
      } else if (ssri.checkData(data, sri)) {
        return data
      } else {
        throw integrityError(sri, cpath)
      }
    })
  })
}

module.exports.stream = readStream
module.exports.readStream = readStream
function readStream (cache, integrity, opts) {
  opts = ReadOpts(opts)
  const stream = new PassThrough()
  pickContentSri(
    cache, integrity
  ).then(content => {
    const sri = content.sri
    return pipe(
      fs.createReadStream(contentPath(cache, sri)),
      ssri.integrityStream({
        integrity: sri,
        size: opts.size
      }),
      stream
    )
  }).catch(err => {
    stream.emit('error', err)
  })
  return stream
}

if (fs.copyFile) {
  module.exports.copy = copy
}
function copy (cache, integrity, dest, opts) {
  opts = ReadOpts(opts)
  return pickContentSri(cache, integrity).then(content => {
    const sri = content.sri
    const cpath = contentPath(cache, sri)
    return fs.copyFileAsync(cpath, dest).then(() => content.size)
  })
}

module.exports.hasContent = hasContent
function hasContent (cache, integrity) {
  if (!integrity) { return BB.resolve(false) }
  return pickContentSri(cache, integrity)
    .catch({code: 'ENOENT'}, () => false)
    .catch({code: 'EPERM'}, err => {
      if (process.platform !== 'win32') {
        throw err
      } else {
        return false
      }
    }).then(content => {
      if (!content.sri) return false
      return ({ sri: content.sri, size: content.stat.size })
    })
}

module.exports._pickContentSri = pickContentSri
function pickContentSri (cache, integrity) {
  const sri = ssri.parse(integrity)
  // If `integrity` has multiple entries, pick the first digest
  // with available local data.
  const algo = sri.pickAlgorithm()
  const digests = sri[algo]
  if (digests.length <= 1) {
    const cpath = contentPath(cache, digests[0])
    return fs.lstatAsync(cpath).then(stat => ({ sri: digests[0], stat }))
  } else {
    return BB.any(sri[sri.pickAlgorithm()].map(meta => {
      return pickContentSri(cache, meta)
    }))
      .catch(err => {
        if ([].some.call(err, e => e.code === 'ENOENT')) {
          throw Object.assign(
            new Error('No matching content found for ' + sri.toString()),
            {code: 'ENOENT'}
          )
        } else {
          throw err[0]
        }
      })
  }
}

function sizeError (expected, found) {
  var err = new Error(Y`Bad data size: expected inserted data to be ${expected} bytes, but got ${found} instead`)
  err.expected = expected
  err.found = found
  err.code = 'EBADSIZE'
  return err
}

function integrityError (sri, path) {
  var err = new Error(Y`Integrity verification failed for ${sri} (${path})`)
  err.code = 'EINTEGRITY'
  err.sri = sri
  err.path = path
  return err
}