memfs icon indicating copy to clipboard operation
memfs copied to clipboard

readonly filesystem functions mutate `mtime` of parent directories

Open 5cover opened this issue 6 months ago • 0 comments

While experimenting with memfs, I noticed a surprising behavior: calling fs.statSync(path) (or likely fs.stat) appears to mutate the mtime of the parent directory, even if no write operation is involved.

This seems counter to expectations — especially for applications treating the in-memory filesystem as read-only or deterministic.

Minimal reproduction

Here’s a minimal script to test and demonstrate the issue:

import { memfs } from 'memfs';
import { dirname } from 'path';

function test(json, path, cwd) {
    const { fs } = memfs(json, cwd);

    const dir = dirname(path);

    // Set known mtime on parent directory
    const initialMtime = 100000;
    fs.utimesSync(dir, initialMtime, initialMtime);

    const mtimeBefore = fs.statSync(dir).mtimeMs;

    // Read metadata of target path
    fs.statSync(path);

    const mtimeAfter = fs.statSync(dir).mtimeMs;

    const mutated = mtimeAfter !== mtimeBefore;
    console.log(path, mutated ? `BUG: mtime changed! ${mtimeBefore} -> ${mtimeAfter}` : 'OK: mtime unchanged.');
    return mutated;
}

// Try on a directory
test({ testDir: {} }, '/testDir');

// Try on a file
test({ testFile: 'hello' }, '/testFile');

Output observed

/testDir BUG: mtime changed! 100000 -> 1750326756327
/testFile BUG: mtime changed! 100000 -> 1750326756330

This suggests that even simple statSync calls are causing the parent directory's mtime to update to the current time.

Edit: this doesn't appear to just affect stat. Other read-only operations like readdir seem to cause the issue

Update: After investigation, I traced the mtime mutation to a specific line in Link.prototype.getChild, where this.getNode().mtime = new Date() is called unconditionally. This happens even during readonly operations like statSync() or readdirSync(), which violates POSIX expectations.

This method is a pure lookup and should not mutate timestamps.

5cover avatar Jun 19 '25 09:06 5cover