Don't follow symlinks when checking for lockfiles

The Supervisor should only care whether a lockfile exists or
not. This also fixes an edge case where a user symlinked a lockfile
to a nonexistent file, causing the Supervisor to enter an error
loop as it was not able to `stat` the nonexistent file.

Change-type: patch
Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
Christina Ying Wang 2024-04-11 17:51:49 -07:00 committed by Felipe Lalanne
parent ef948c7fa2
commit 6e185fbd44
2 changed files with 18 additions and 2 deletions

View File

@ -29,8 +29,10 @@ export const getLocksTaken = async (
}
for (const fileOrDir of filesOrDirs) {
const lockPath = `${rootDir}/${fileOrDir.name}`;
// A lock is taken if it's a file or directory within rootDir that passes filter fn
if (lockFilter(lockPath, await fs.stat(lockPath))) {
// A lock is taken if it's a file or directory within rootDir that passes filter fn.
// We also don't want to follow symlinks since we don't want to follow the lock to
// the target path if it's a symlink and only care that it exists or not.
if (lockFilter(lockPath, await fs.lstat(lockPath))) {
locksTaken.push(lockPath);
// Otherwise, if non-lock directory, seek locks recursively within directory
} else if (fileOrDir.isDirectory()) {

View File

@ -226,4 +226,18 @@ describe('lib/lockfile', () => {
// Clean up locks
await fs.rm(`${lockdir}`, { recursive: true });
});
// This tests an edge case where the lockfile is a symlink to a nonexistent file.
// Calling fs.stat on such a lockfile will throw, hence why we switched to fs.lstat.
it('should not error if lockfile is a symlink to a nonexistent file', async () => {
// Create symlink lock
await fs.symlink('/nonexistent', `${lockdir}/updates.lock`);
expect(
await lockfile.getLocksTaken(lockdir, (_p, s) => s.isSymbolicLink()),
).to.have.members([`${lockdir}/updates.lock`]);
// Cleanup symlink lock
await fs.rm(`${lockdir}/updates.lock`);
});
});