From 5c5059a69c2aaaedfe4e9766e102ae9fb79e8255 Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 10 Aug 2021 15:07:40 -0700 Subject: [PATCH] fix: reserve paths case-insensitively The path reservation system prevents multiple file entries clobbering one another while in flight, while allowing maximal parallelization in unpacking otherwise. However, on case-insensitive file systems, this can still introduce a race condition if the entries in the archive have different cases, as capital and lowercase entries will not be treated as reserving the same path. Normalize slashes and cases immediately when reserving paths, such that paths which differ only in case or slash repetition are treated as identical paths. --- lib/path-reservations.js | 7 ++++--- test/path-reservations.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/path-reservations.js b/lib/path-reservations.js index 7ea03829..48c750e3 100644 --- a/lib/path-reservations.js +++ b/lib/path-reservations.js @@ -8,6 +8,7 @@ const assert = require('assert') const normPath = require('./normalize-windows-path.js') +const { join } = require('path') module.exports = () => { // path => [function or Set] @@ -19,10 +20,9 @@ module.exports = () => { const reservations = new Map() // return a set of parent dirs for a given path - const { join } = require('path') const getDirs = path => - normPath(join(path)).split('/').slice(0, -1).reduce((set, path) => - set.length ? set.concat(normPath(join(set[set.length-1], path))) + path.split('/').slice(0, -1).reduce((set, path) => + set.length ? set.concat(normPath(join(set[set.length - 1], path))) : [path], []) // functions currently running @@ -99,6 +99,7 @@ module.exports = () => { } const reserve = (paths, fn) => { + paths = paths.map(p => normPath(join(p)).toLowerCase()) const dirs = new Set( paths.map(path => getDirs(path)).reduce((a, b) => a.concat(b)) ) diff --git a/test/path-reservations.js b/test/path-reservations.js index f2dfd9d2..9ce46b80 100644 --- a/test/path-reservations.js +++ b/test/path-reservations.js @@ -49,7 +49,7 @@ t.test('basic race', t => { } t.ok(reserve(['a/b/c/d'], file), 'file starts right away') - t.notOk(reserve(['a/b/c/d', 'a/b/e'], link), 'link waits') + t.notOk(reserve(['a/B/c////D', 'a/b/e'], link), 'link waits') t.notOk(reserve(['a/b/e/f'], dir), 'dir waits') t.notOk(reserve(['a/b'], dir2), 'dir2 waits') t.notOk(reserve(['a/b/x'], dir3), 'dir3 waits')