Skip to content

Commit

Permalink
fix: reserve paths case-insensitively
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
isaacs committed Aug 11, 2021
1 parent 8d2a316 commit 32027d6
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 3 deletions.
5 changes: 3 additions & 2 deletions lib/path-reservations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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], [])

Expand Down Expand Up @@ -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))
)
Expand Down
2 changes: 1 addition & 1 deletion test/path-reservations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down

0 comments on commit 32027d6

Please sign in to comment.