From 32027d6b85c70e04eab9b154a7e11d06eba758b8 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 | 5 +++-- test/path-reservations.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/path-reservations.js b/lib/path-reservations.js index 3cdf1e23..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,9 +20,8 @@ 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) => + path.split('/').slice(0, -1).reduce((set, path) => set.length ? set.concat(normPath(join(set[set.length - 1], path))) : [path], []) @@ -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')