diff options
Diffstat (limited to 'deps/npm/node_modules/cacache/lib/verify.js')
-rw-r--r-- | deps/npm/node_modules/cacache/lib/verify.js | 348 |
1 files changed, 156 insertions, 192 deletions
diff --git a/deps/npm/node_modules/cacache/lib/verify.js b/deps/npm/node_modules/cacache/lib/verify.js index a39fb6ce1d..52692a01d1 100644 --- a/deps/npm/node_modules/cacache/lib/verify.js +++ b/deps/npm/node_modules/cacache/lib/verify.js @@ -5,7 +5,7 @@ const util = require('util') const pMap = require('p-map') const contentPath = require('./content/path') const fixOwner = require('./util/fix-owner') -const fs = require('fs') +const fs = require('@npmcli/fs') const fsm = require('fs-minipass') const glob = util.promisify(require('glob')) const index = require('./entry-index') @@ -18,11 +18,6 @@ const globify = pattern => pattern.split('\\').join('/') const hasOwnProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key) -const stat = util.promisify(fs.stat) -const truncate = util.promisify(fs.truncate) -const writeFile = util.promisify(fs.writeFile) -const readFile = util.promisify(fs.readFile) - const verifyOpts = (opts) => ({ concurrency: 20, log: { silly () {} }, @@ -31,7 +26,7 @@ const verifyOpts = (opts) => ({ module.exports = verify -function verify (cache, opts) { +async function verify (cache, opts) { opts = verifyOpts(opts) opts.log.silly('verify', 'verifying cache at', cache) @@ -45,56 +40,47 @@ function verify (cache, opts) { markEndTime, ] - return steps - .reduce((promise, step, i) => { - const label = step.name - const start = new Date() - return promise.then((stats) => { - return step(cache, opts).then((s) => { - s && - Object.keys(s).forEach((k) => { - stats[k] = s[k] - }) - const end = new Date() - if (!stats.runTime) { - stats.runTime = {} - } - - stats.runTime[label] = end - start - return Promise.resolve(stats) - }) + const stats = {} + for (const step of steps) { + const label = step.name + const start = new Date() + const s = await step(cache, opts) + if (s) { + Object.keys(s).forEach((k) => { + stats[k] = s[k] }) - }, Promise.resolve({})) - .then((stats) => { - stats.runTime.total = stats.endTime - stats.startTime - opts.log.silly( - 'verify', - 'verification finished for', - cache, - 'in', - `${stats.runTime.total}ms` - ) - return stats - }) + } + const end = new Date() + if (!stats.runTime) { + stats.runTime = {} + } + stats.runTime[label] = end - start + } + stats.runTime.total = stats.endTime - stats.startTime + opts.log.silly( + 'verify', + 'verification finished for', + cache, + 'in', + `${stats.runTime.total}ms` + ) + return stats } -function markStartTime (cache, opts) { - return Promise.resolve({ startTime: new Date() }) +async function markStartTime (cache, opts) { + return { startTime: new Date() } } -function markEndTime (cache, opts) { - return Promise.resolve({ endTime: new Date() }) +async function markEndTime (cache, opts) { + return { endTime: new Date() } } -function fixPerms (cache, opts) { +async function fixPerms (cache, opts) { opts.log.silly('verify', 'fixing cache permissions') - return fixOwner - .mkdirfix(cache, cache) - .then(() => { - // TODO - fix file permissions too - return fixOwner.chownr(cache, cache) - }) - .then(() => null) + await fixOwner.mkdirfix(cache, cache) + // TODO - fix file permissions too + await fixOwner.chownr(cache, cache) + return null } // Implements a naive mark-and-sweep tracing garbage collector. @@ -106,7 +92,7 @@ function fixPerms (cache, opts) { // 4. If content is live, verify its checksum and delete it if it fails // 5. If content is not marked as live, rimraf it. // -function garbageCollect (cache, opts) { +async function garbageCollect (cache, opts) { opts.log.silly('verify', 'garbage collecting content') const indexStream = index.lsStream(cache) const liveContent = new Set() @@ -117,156 +103,135 @@ function garbageCollect (cache, opts) { liveContent.add(entry.integrity.toString()) }) - return new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { indexStream.on('end', resolve).on('error', reject) - }).then(() => { - const contentDir = contentPath.contentDir(cache) - return glob(globify(path.join(contentDir, '**')), { - follow: false, - nodir: true, - nosort: true, - }).then((files) => { - return Promise.resolve({ - verifiedContent: 0, - reclaimedCount: 0, - reclaimedSize: 0, - badContentCount: 0, - keptSize: 0, - }).then((stats) => - pMap( - files, - (f) => { - const split = f.split(/[/\\]/) - const digest = split.slice(split.length - 3).join('') - const algo = split[split.length - 4] - const integrity = ssri.fromHex(digest, algo) - if (liveContent.has(integrity.toString())) { - return verifyContent(f, integrity).then((info) => { - if (!info.valid) { - stats.reclaimedCount++ - stats.badContentCount++ - stats.reclaimedSize += info.size - } else { - stats.verifiedContent++ - stats.keptSize += info.size - } - return stats - }) - } else { - // No entries refer to this content. We can delete. - stats.reclaimedCount++ - return stat(f).then((s) => { - return rimraf(f).then(() => { - stats.reclaimedSize += s.size - return stats - }) - }) - } - }, - { concurrency: opts.concurrency } - ).then(() => stats) - ) - }) }) -} - -function verifyContent (filepath, sri) { - return stat(filepath) - .then((s) => { - const contentInfo = { - size: s.size, - valid: true, - } - return ssri - .checkStream(new fsm.ReadStream(filepath), sri) - .catch((err) => { - if (err.code !== 'EINTEGRITY') { - throw err - } - - return rimraf(filepath).then(() => { - contentInfo.valid = false - }) - }) - .then(() => contentInfo) - }) - .catch((err) => { - if (err.code === 'ENOENT') { - return { size: 0, valid: false } + const contentDir = contentPath.contentDir(cache) + const files = await glob(globify(path.join(contentDir, '**')), { + follow: false, + nodir: true, + nosort: true, + }) + const stats = { + verifiedContent: 0, + reclaimedCount: 0, + reclaimedSize: 0, + badContentCount: 0, + keptSize: 0, + } + await pMap( + files, + async (f) => { + const split = f.split(/[/\\]/) + const digest = split.slice(split.length - 3).join('') + const algo = split[split.length - 4] + const integrity = ssri.fromHex(digest, algo) + if (liveContent.has(integrity.toString())) { + const info = await verifyContent(f, integrity) + if (!info.valid) { + stats.reclaimedCount++ + stats.badContentCount++ + stats.reclaimedSize += info.size + } else { + stats.verifiedContent++ + stats.keptSize += info.size + } + } else { + // No entries refer to this content. We can delete. + stats.reclaimedCount++ + const s = await fs.stat(f) + await rimraf(f) + stats.reclaimedSize += s.size } + return stats + }, + { concurrency: opts.concurrency } + ) + return stats +} +async function verifyContent (filepath, sri) { + const contentInfo = {} + try { + const { size } = await fs.stat(filepath) + contentInfo.size = size + contentInfo.valid = true + await ssri.checkStream(new fsm.ReadStream(filepath), sri) + } catch (err) { + if (err.code === 'ENOENT') { + return { size: 0, valid: false } + } + if (err.code !== 'EINTEGRITY') { throw err - }) + } + + await rimraf(filepath) + contentInfo.valid = false + } + return contentInfo } -function rebuildIndex (cache, opts) { +async function rebuildIndex (cache, opts) { opts.log.silly('verify', 'rebuilding index') - return index.ls(cache).then((entries) => { - const stats = { - missingContent: 0, - rejectedEntries: 0, - totalEntries: 0, - } - const buckets = {} - for (const k in entries) { - /* istanbul ignore else */ - if (hasOwnProperty(entries, k)) { - const hashed = index.hashKey(k) - const entry = entries[k] - const excluded = opts.filter && !opts.filter(entry) - excluded && stats.rejectedEntries++ - if (buckets[hashed] && !excluded) { - buckets[hashed].push(entry) - } else if (buckets[hashed] && excluded) { - // skip - } else if (excluded) { - buckets[hashed] = [] - buckets[hashed]._path = index.bucketPath(cache, k) - } else { - buckets[hashed] = [entry] - buckets[hashed]._path = index.bucketPath(cache, k) - } + const entries = await index.ls(cache) + const stats = { + missingContent: 0, + rejectedEntries: 0, + totalEntries: 0, + } + const buckets = {} + for (const k in entries) { + /* istanbul ignore else */ + if (hasOwnProperty(entries, k)) { + const hashed = index.hashKey(k) + const entry = entries[k] + const excluded = opts.filter && !opts.filter(entry) + excluded && stats.rejectedEntries++ + if (buckets[hashed] && !excluded) { + buckets[hashed].push(entry) + } else if (buckets[hashed] && excluded) { + // skip + } else if (excluded) { + buckets[hashed] = [] + buckets[hashed]._path = index.bucketPath(cache, k) + } else { + buckets[hashed] = [entry] + buckets[hashed]._path = index.bucketPath(cache, k) } } - return pMap( - Object.keys(buckets), - (key) => { - return rebuildBucket(cache, buckets[key], stats, opts) - }, - { concurrency: opts.concurrency } - ).then(() => stats) - }) + } + await pMap( + Object.keys(buckets), + (key) => { + return rebuildBucket(cache, buckets[key], stats, opts) + }, + { concurrency: opts.concurrency } + ) + return stats } -function rebuildBucket (cache, bucket, stats, opts) { - return truncate(bucket._path).then(() => { - // This needs to be serialized because cacache explicitly - // lets very racy bucket conflicts clobber each other. - return bucket.reduce((promise, entry) => { - return promise.then(() => { - const content = contentPath(cache, entry.integrity) - return stat(content) - .then(() => { - return index - .insert(cache, entry.key, entry.integrity, { - metadata: entry.metadata, - size: entry.size, - }) - .then(() => { - stats.totalEntries++ - }) - }) - .catch((err) => { - if (err.code === 'ENOENT') { - stats.rejectedEntries++ - stats.missingContent++ - return - } - throw err - }) +async function rebuildBucket (cache, bucket, stats, opts) { + await fs.truncate(bucket._path) + // This needs to be serialized because cacache explicitly + // lets very racy bucket conflicts clobber each other. + for (const entry of bucket) { + const content = contentPath(cache, entry.integrity) + try { + await fs.stat(content) + await index.insert(cache, entry.key, entry.integrity, { + metadata: entry.metadata, + size: entry.size, }) - }, Promise.resolve()) - }) + stats.totalEntries++ + } catch (err) { + if (err.code === 'ENOENT') { + stats.rejectedEntries++ + stats.missingContent++ + } else { + throw err + } + } + } } function cleanTmp (cache, opts) { @@ -278,7 +243,7 @@ function writeVerifile (cache, opts) { const verifile = path.join(cache, '_lastverified') opts.log.silly('verify', 'writing verifile to ' + verifile) try { - return writeFile(verifile, '' + +new Date()) + return fs.writeFile(verifile, `${Date.now()}`) } finally { fixOwner.chownr.sync(cache, verifile) } @@ -286,8 +251,7 @@ function writeVerifile (cache, opts) { module.exports.lastRun = lastRun -function lastRun (cache) { - return readFile(path.join(cache, '_lastverified'), 'utf8').then( - (data) => new Date(+data) - ) +async function lastRun (cache) { + const data = await fs.readFile(path.join(cache, '_lastverified'), { encoding: 'utf8' }) + return new Date(+data) } |