| ... | ... | @@ -70,8 +70,9 @@ async function pathExists(target: string): Promise<boolean> { |
| 70 | 70 | } |
| 71 | 71 | } |
| 72 | 72 | |
| 73 | // Opened read-write: Windows refuses to flush a handle without write access. |
| 73 | 74 | async function syncFile(filePath: string): Promise<void> { |
| 74 | | const handle = await NodeFSP.open(filePath, "r"); |
| 75 | const handle = await NodeFSP.open(filePath, "r+"); |
| 75 | 76 | try { |
| 76 | 77 | await handle.sync(); |
| 77 | 78 | } finally { |
| ... | ... | @@ -79,10 +80,15 @@ async function syncFile(filePath: string): Promise<void> { |
| 79 | 80 | } |
| 80 | 81 | } |
| 81 | 82 | |
| 83 | // Flushes a directory entry so a rename into it survives power loss. Windows |
| 84 | // has no directory fsync: the handle opens but sync fails with EPERM, and |
| 85 | // NTFS journals the rename on its own. |
| 82 | 86 | async function syncDirectory(directory: string): Promise<void> { |
| 83 | 87 | const handle = await NodeFSP.open(directory, "r"); |
| 84 | 88 | try { |
| 85 | 89 | await handle.sync(); |
| 90 | } catch (error) { |
| 91 | if ((error as NodeJS.ErrnoException).code !== "EPERM") throw error; |
| 86 | 92 | } finally { |
| 87 | 93 | await handle.close(); |
| 88 | 94 | } |
| ... | ... | @@ -188,12 +194,7 @@ export async function writeServiceState(filePath: string, state: ServiceState): |
| 188 | 194 | await handle.close(); |
| 189 | 195 | handle = undefined; |
| 190 | 196 | await NodeFSP.rename(tempPath, filePath); |
| 191 | | const directoryHandle = await NodeFSP.open(directory, "r"); |
| 192 | | try { |
| 193 | | await directoryHandle.sync(); |
| 194 | | } finally { |
| 195 | | await directoryHandle.close(); |
| 196 | | } |
| 197 | await syncDirectory(directory); |
| 197 | 198 | } finally { |
| 198 | 199 | await handle?.close().catch(() => undefined); |
| 199 | 200 | await NodeFSP.rm(tempPath, { force: true }).catch(() => undefined); |