Blame view

node_modules/cacache/lib/content/read.js 3.32 KB
aaac7fed   liuqimichale   add
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
  'use strict'
  
  const BB = require('bluebird')
  
  const contentPath = require('./path')
  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)
  
  module.exports = read
  function read (cache, integrity, opts) {
    opts = 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 = 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 = 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
  }