From 568377f7f06d6c9514279200b7cffea177fc26f1 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 17 Jun 2024 16:28:12 +0200 Subject: [PATCH] fs: do not crash if the watched file is removed while setting up watch Signed-off-by: Matteo Collina PR-URL: https://github.com/nodejs/node/pull/53452 Reviewed-By: James M Snell Reviewed-By: Moshe Atlow Reviewed-By: Marco Ippolito Reviewed-By: Chemi Atlow Reviewed-By: Luigi Pinca Reviewed-By: Yagiz Nizipli --- lib/internal/fs/recursive_watch.js | 17 +++++++--- ...s-watch-recursive-linux-parallel-remove.js | 33 +++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-fs-watch-recursive-linux-parallel-remove.js diff --git a/lib/internal/fs/recursive_watch.js b/lib/internal/fs/recursive_watch.js index d312e54013cfb9..38c0505797c3ad 100644 --- a/lib/internal/fs/recursive_watch.js +++ b/lib/internal/fs/recursive_watch.js @@ -83,7 +83,7 @@ class FSWatcher extends EventEmitter { this.#closed = true; for (const file of this.#files.keys()) { - this.#watchers.get(file).close(); + this.#watchers.get(file)?.close(); this.#watchers.delete(file); } @@ -98,7 +98,7 @@ class FSWatcher extends EventEmitter { for (const filename of this.#files.keys()) { if (StringPrototypeStartsWith(filename, file)) { this.#files.delete(filename); - this.#watchers.get(filename).close(); + this.#watchers.get(filename)?.close(); this.#watchers.delete(filename); } } @@ -126,9 +126,16 @@ class FSWatcher extends EventEmitter { this.#symbolicFiles.add(f); } - this.#watchFile(f); - if (file.isDirectory() && !file.isSymbolicLink()) { - this.#watchFolder(f); + try { + this.#watchFile(f); + if (file.isDirectory() && !file.isSymbolicLink()) { + this.#watchFolder(f); + } + } catch (err) { + // Ignore ENOENT + if (err.code !== 'ENOENT') { + throw err; + } } } } diff --git a/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js b/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js new file mode 100644 index 00000000000000..145b3314f24b59 --- /dev/null +++ b/test/parallel/test-fs-watch-recursive-linux-parallel-remove.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); + +if (!common.isLinux) + common.skip('This test can run only on Linux'); + +// Test that the watcher do not crash if the file "disappears" while +// watch is being set up. + +const path = require('node:path'); +const fs = require('node:fs'); +const { spawn } = require('node:child_process'); + +const tmpdir = require('../common/tmpdir'); +const testDir = tmpdir.path; +tmpdir.refresh(); + +const watcher = fs.watch(testDir, { recursive: true }); +watcher.on('change', function(event, filename) { + // This console.log makes the error happen + // do not remove + console.log(filename, event); +}); + +const testFile = path.join(testDir, 'a'); +const child = spawn(process.argv[0], ['-e', `const fs = require('node:fs'); for (let i = 0; i < 10000; i++) { const fd = fs.openSync('${testFile}', 'w'); fs.writeSync(fd, Buffer.from('hello')); fs.rmSync('${testFile}') }`], { + stdio: 'inherit' +}); + +child.on('exit', function() { + watcher.close(); +});