From 67d29a8e77373206823ea3d5b9e8a84156be6116 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Thu, 5 Mar 2026 15:13:36 +0000 Subject: [PATCH] fix(mod_unpack): flush directory metadata on XFS before reading and use readdirSync-based iteration to avoid missing entries when unpacking --- changelog.md | 7 ++++++ ts/00_commitinfo_data.ts | 2 +- ts/mod_unpack/classes.tsunpacker.ts | 39 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/changelog.md b/changelog.md index f350dbb..1683000 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-03-05 - 4.1.25 - fix(mod_unpack) +flush directory metadata on XFS before reading and use readdirSync-based iteration to avoid missing entries when unpacking + +- Call fs.fsyncSync on destination and nested directory file descriptors to force XFS to commit delayed directory metadata (addresses XFS CIL delayed logging causing incomplete readdir/opendir results). +- Replace opendirSync/readSync loops with readdirSync-based iteration for simpler, deterministic directory listing. +- Remove unused moved counter and update diagnostic log to report nestedEntries.length for moved entry count. + ## 2026-03-05 - 4.1.24 - fix(mod_unpack) iterate directories with opendirSync/readSync to avoid missing entries on XFS and ensure directory handles are closed diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 2ae2b27..8359714 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/tsbuild', - version: '4.1.24', + version: '4.1.25', description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.' } diff --git a/ts/mod_unpack/classes.tsunpacker.ts b/ts/mod_unpack/classes.tsunpacker.ts index 1e0c954..7094ef1 100644 --- a/ts/mod_unpack/classes.tsunpacker.ts +++ b/ts/mod_unpack/classes.tsunpacker.ts @@ -107,39 +107,40 @@ export class TsUnpacker { const nestedPath = this.getNestedPath(); + // Force XFS to flush pending directory metadata before reading. + // XFS delayed logging (CIL) can defer metadata commits, causing + // readdirSync/opendirSync to return incomplete results immediately + // after TypeScript's emit() creates files via writeFileSync. + const destFd = fs.openSync(this.destDir, 'r'); + fs.fsyncSync(destFd); + fs.closeSync(destFd); + const nestedFd = fs.openSync(nestedPath, 'r'); + fs.fsyncSync(nestedFd); + fs.closeSync(nestedFd); + // Step 1: Remove sibling entries (everything in dest except the source folder) - // Use opendirSync to keep a single directory handle open for reliable iteration - const destDir = fs.opendirSync(this.destDir); - let destEntry; - while ((destEntry = destDir.readSync()) !== null) { - if (destEntry.name !== this.sourceFolderName) { - fs.rmSync(path.join(this.destDir, destEntry.name), { recursive: true, force: true }); + const destEntries = fs.readdirSync(this.destDir); + for (const entry of destEntries) { + if (entry !== this.sourceFolderName) { + fs.rmSync(path.join(this.destDir, entry), { recursive: true, force: true }); } } - destDir.closeSync(); // Step 2: Move all contents from nested dir up to dest dir - // Use opendirSync to keep a single directory handle open — this avoids - // partial results from readdirSync which opens a fresh file descriptor - // each call and can miss entries on XFS with delayed metadata logging - const nestedDir = fs.opendirSync(nestedPath); - let nestedEntry; - let moved = 0; - while ((nestedEntry = nestedDir.readSync()) !== null) { + const nestedEntries = fs.readdirSync(nestedPath); + for (const entry of nestedEntries) { fs.renameSync( - path.join(nestedPath, nestedEntry.name), - path.join(this.destDir, nestedEntry.name), + path.join(nestedPath, entry), + path.join(this.destDir, entry), ); - moved++; } - nestedDir.closeSync(); // Step 3: Remove the now-empty nested directory fs.rmdirSync(nestedPath); // Diagnostic: verify final state const finalEntries = fs.readdirSync(this.destDir); - console.log(` 📦 Unpacked ${this.sourceFolderName}: moved ${moved} entries, final: ${finalEntries.length} entries`); + console.log(` 📦 Unpacked ${this.sourceFolderName}: moved ${nestedEntries.length} entries, final: ${finalEntries.length} entries`); return true; }