Skip to content

Commit 2d0d5b2

Browse files
authored
File watcher stops working with malformed workspace file (fix microsoft#153881) (microsoft#154231)
1 parent c7e5301 commit 2d0d5b2

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as parcelWatcher from '@parcel/watcher';
7-
import { existsSync, unlinkSync } from 'fs';
7+
import { existsSync, statSync, unlinkSync } from 'fs';
88
import { tmpdir } from 'os';
99
import { DeferredPromise, RunOnceScheduler, ThrottledWorker } from 'vs/base/common/async';
1010
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
@@ -653,7 +653,7 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
653653
}
654654
}
655655

656-
protected normalizeRequests(requests: IRecursiveWatchRequest[]): IRecursiveWatchRequest[] {
656+
protected normalizeRequests(requests: IRecursiveWatchRequest[], validatePaths = true): IRecursiveWatchRequest[] {
657657
const requestTrie = TernarySearchTree.forPaths<IRecursiveWatchRequest>(!isLinux);
658658

659659
// Sort requests by path length to have shortest first
@@ -674,16 +674,35 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
674674
continue; // path is ignored entirely (via `**` glob exclude)
675675
}
676676

677+
// Check for overlapping requests
677678
if (requestTrie.findSubstr(request.path)) {
678679
try {
679680
const realpath = realpathSync(request.path);
680681
if (realpath === request.path) {
681682
this.trace(`ignoring a path for watching who's parent is already watched: ${request.path}`);
682683

683-
continue; // path is not a symbolic link or similar
684+
continue;
684685
}
685686
} catch (error) {
686-
continue; // invalid path - ignore from watching
687+
this.trace(`ignoring a path for watching who's realpath failed to resolve: ${request.path} (error: ${error})`);
688+
689+
continue;
690+
}
691+
}
692+
693+
// Check for invalid paths
694+
if (validatePaths) {
695+
try {
696+
const stat = statSync(request.path);
697+
if (!stat.isDirectory()) {
698+
this.trace(`ignoring a path for watching that is a file and not a folder: ${request.path}`);
699+
700+
continue;
701+
}
702+
} catch (error) {
703+
this.trace(`ignoring a path for watching who's stat info failed to resolve: ${request.path} (error: ${error})`);
704+
705+
continue;
687706
}
688707
}
689708

src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { ltrim } from 'vs/base/common/strings';
3333
return { path, excludes, recursive: true };
3434
});
3535

36-
return this.normalizeRequests(requests).map(request => request.path);
36+
return this.normalizeRequests(requests, false /* validate paths skipped for tests */).map(request => request.path);
3737
}
3838

3939
override async watch(requests: IRecursiveWatchRequest[]): Promise<void> {
@@ -155,7 +155,7 @@ import { ltrim } from 'vs/base/common/strings';
155155
}
156156

157157
test('basics', async function () {
158-
await watcher.watch([{ path: testDir, excludes: [], recursive: true }]); //
158+
await watcher.watch([{ path: testDir, excludes: [], recursive: true }]);
159159

160160
// New file
161161
const newFilePath = join(testDir, 'deep', 'newFile.txt');
@@ -430,6 +430,16 @@ import { ltrim } from 'vs/base/common/strings';
430430
await changeFuture;
431431
});
432432

433+
test('invalid path does not crash watcher', async function () {
434+
await watcher.watch([
435+
{ path: testDir, excludes: [], recursive: true },
436+
{ path: join(testDir, 'invalid-folder'), excludes: [], recursive: true },
437+
{ path: __filename, excludes: [], recursive: true }
438+
]);
439+
440+
return basicCrudTest(join(testDir, 'deep', 'newFile.txt'));
441+
});
442+
433443
test('subsequent watch updates watchers (excludes)', async function () {
434444
await watcher.watch([{ path: testDir, excludes: [realpathSync(testDir)], recursive: true }]);
435445
await watcher.watch([{ path: testDir, excludes: [], recursive: true }]);

0 commit comments

Comments
 (0)