From ecbe5b096a728c569ba96aa1d4fe73cf5ba12169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Csahvx655-wq=E2=80=9D?= <“sahvx655@gmail.com”> Date: Fri, 29 May 2026 12:31:09 +0530 Subject: [PATCH] Ensure atomic session persistence in FileStore using write-then-rename pattern --- .../apache/catalina/session/FileStore.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java index 0ec92f10c522..9081357992bd 100644 --- a/java/org/apache/catalina/session/FileStore.java +++ b/java/org/apache/catalina/session/FileStore.java @@ -24,6 +24,9 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; @@ -269,13 +272,28 @@ public void save(Session session) throws IOException { .trace(sm.getString(getStoreName() + ".saving", session.getIdInternal(), file.getAbsolutePath())); } + File tempFile = new File(file.getAbsolutePath() + ".tmp"); + Lock writeLock = sessionLocksById.getLock(session.getIdInternal()).writeLock(); writeLock.lock(); try { - try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath()); + try (FileOutputStream fos = new FileOutputStream(tempFile); ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos))) { ((StandardSession) session).writeObjectData(oos); } + try { + try { + Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException e) { + Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + if (tempFile.exists() && !tempFile.delete()) { + log.warn(sm.getString("fileStore.deleteFailed", tempFile)); + } + throw e; + } } finally { writeLock.unlock(); }