From 8599e563e0b59de5074ffdf4d4ab6cf3e605e89c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 01:43:05 +0000 Subject: [PATCH 1/4] Initial plan From 9c335b18a428dce52a440bd70fb6645fa7ab804a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 01:49:17 +0000 Subject: [PATCH 2/4] Fix: Delete existing file before extracting to handle read-only files Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- .../plexus/archiver/AbstractUnArchiver.java | 9 ++++-- .../archiver/AbstractUnArchiverTest.java | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java index c61327c5..d529e622 100644 --- a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java +++ b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java @@ -38,8 +38,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; - // TODO there should really be constructors which take the source file. /** @@ -324,7 +322,12 @@ protected void extractFile( } else if (isDirectory) { targetFileName.mkdirs(); } else { - Files.copy(compressedInputStream, targetFileName.toPath(), REPLACE_EXISTING); + // Delete existing file first to handle read-only files + // This matches the behavior of tar and unzip + if (targetFileName.exists()) { + targetFileName.delete(); + } + Files.copy(compressedInputStream, targetFileName.toPath()); } targetFileName.setLastModified(entryDate.getTime()); diff --git a/src/test/java/org/codehaus/plexus/archiver/AbstractUnArchiverTest.java b/src/test/java/org/codehaus/plexus/archiver/AbstractUnArchiverTest.java index a73ac715..3c57c541 100644 --- a/src/test/java/org/codehaus/plexus/archiver/AbstractUnArchiverTest.java +++ b/src/test/java/org/codehaus/plexus/archiver/AbstractUnArchiverTest.java @@ -188,4 +188,33 @@ void shouldExtractWhenCasingDifferOnlyInEntryNamePath(@TempDir File temporaryFol assertTrue(abstractUnArchiver.shouldExtractEntry(temporaryFolder, file, entryName, entryDate)); assertEquals(0, abstractUnArchiver.casingMessageEmitted.get()); } + + @Test + void shouldExtractReadOnlyFile(@TempDir File temporaryFolder) throws Exception { + // given + File readOnlyFile = new File(temporaryFolder, "readonly.txt"); + readOnlyFile.createNewFile(); + java.nio.file.Files.write(readOnlyFile.toPath(), "original content".getBytes()); + + // Make the file read-only (simulate -r-xr-xr-x permissions) + readOnlyFile.setWritable(false); + assertTrue(readOnlyFile.exists()); + assertFalse(readOnlyFile.canWrite()); + + // Create a mock input stream with new content + String newContent = "new content"; + java.io.InputStream inputStream = new java.io.ByteArrayInputStream(newContent.getBytes()); + + // when + abstractUnArchiver.setOverwrite(true); + abstractUnArchiver.extractFile( + null, temporaryFolder, inputStream, "readonly.txt", new Date(), false, null, null, null); + + // then + // The file should have been successfully overwritten + assertTrue(readOnlyFile.exists()); + byte[] actualBytes = java.nio.file.Files.readAllBytes(readOnlyFile.toPath()); + String actualContent = new String(actualBytes); + assertEquals(newContent, actualContent); + } } From 882221ce02edde02b4b4c0f7f6c9b53073531b4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 01:52:23 +0000 Subject: [PATCH 3/4] Address code review: Keep REPLACE_EXISTING as fallback for robustness Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- .../java/org/codehaus/plexus/archiver/AbstractUnArchiver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java index d529e622..6c93f409 100644 --- a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java +++ b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java @@ -38,6 +38,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + // TODO there should really be constructors which take the source file. /** @@ -327,7 +329,7 @@ protected void extractFile( if (targetFileName.exists()) { targetFileName.delete(); } - Files.copy(compressedInputStream, targetFileName.toPath()); + Files.copy(compressedInputStream, targetFileName.toPath(), REPLACE_EXISTING); } targetFileName.setLastModified(entryDate.getTime()); From 242cfe9c84c4aa5d6b19d427da99ee78fb997f54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 02:08:39 +0000 Subject: [PATCH 4/4] Fix: Make file writable before delete on Windows for read-only files Co-authored-by: slachiewicz <6705942+slachiewicz@users.noreply.github.com> --- .../java/org/codehaus/plexus/archiver/AbstractUnArchiver.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java index 6c93f409..9f0cf2c8 100644 --- a/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java +++ b/src/main/java/org/codehaus/plexus/archiver/AbstractUnArchiver.java @@ -327,6 +327,8 @@ protected void extractFile( // Delete existing file first to handle read-only files // This matches the behavior of tar and unzip if (targetFileName.exists()) { + // Make file writable before deleting (required on Windows for read-only files) + targetFileName.setWritable(true); targetFileName.delete(); } Files.copy(compressedInputStream, targetFileName.toPath(), REPLACE_EXISTING);