This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-fileupload.git


The following commit(s) were added to refs/heads/master by this push:
     new 38bdc5a8 Create temporary upload file with owner-only permissions 
(#473).
38bdc5a8 is described below

commit 38bdc5a82cc44b1d301771f97dcee88e1127e363
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Jun 21 11:58:21 2026 +0000

    Create temporary upload file with owner-only permissions (#473).
    
    - Javadoc
    - Formatting
---
 .../fileupload2/core/DeferrableOutputStream.java   |   7 +-
 .../core/DeferrableOutputStreamTest.java           | 437 ++++++++++-----------
 src/changes/changes.xml                            |   3 +-
 3 files changed, 222 insertions(+), 225 deletions(-)

diff --git 
a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DeferrableOutputStream.java
 
b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DeferrableOutputStream.java
index c75ad5d0..3c03b6bb 100644
--- 
a/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DeferrableOutputStream.java
+++ 
b/commons-fileupload2-core/src/main/java/org/apache/commons/fileupload2/core/DeferrableOutputStream.java
@@ -397,18 +397,15 @@ public class DeferrableOutputStream extends OutputStream {
         final EnumSet<StandardOpenOption> options = 
EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, 
StandardOpenOption.WRITE);
         final OutputStream os;
         if (p.getFileSystem().supportedFileAttributeViews().contains("posix")) 
{
-            os = Channels.newOutputStream(Files.newByteChannel(p, options,
-                    
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------"))));
+            os = Channels.newOutputStream(Files.newByteChannel(p, options, 
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-------"))));
         } else {
             os = Channels.newOutputStream(Files.newByteChannel(p, options));
         }
         if (baos != null) {
             baos.writeTo(os);
         }
-
         /**
-         * At this point, the output file has been successfully created,
-         * and we can safely switch state.
+         * At this point, the output file has been successfully created, and 
we can safely switch state.
          */
         state = State.persisted;
         wasPersisted = true;
diff --git 
a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/DeferrableOutputStreamTest.java
 
b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/DeferrableOutputStreamTest.java
index e0c5f9fc..cfed3396 100644
--- 
a/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/DeferrableOutputStreamTest.java
+++ 
b/commons-fileupload2-core/src/test/java/org/apache/commons/fileupload2/core/DeferrableOutputStreamTest.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.commons.fileupload2.core;
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -43,236 +44,234 @@ import 
org.apache.commons.fileupload2.core.DeferrableOutputStream.State;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
-
 /**
- * Test suite for the {@link DeferrableOutputStream}.
+ * Tests {@link DeferrableOutputStream}.
  */
 class DeferrableOutputStreamTest {
-       private static final Path testDir = 
Paths.get("target/unit-tests/DeferrableOutputStreamTest");
-       private static Path tempTestDir;
-       @BeforeAll
-       static void setUpTestDirs() throws IOException {
-               Files.createDirectories(testDir);
-               tempTestDir = Files.createTempDirectory(testDir, "testDir");
-       }
-
-       private Supplier<Path> testFileSupplier = () -> {
-               try {
-                       return Files.createTempFile(tempTestDir, "testFile", 
".bin");
-               } catch (IOException ioe) {
-                       throw new UncheckedIOException(ioe);
-               }
-       };
 
-       protected byte[] read(InputStream pIs) throws IOException {
-               final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-               final byte[] buffer = new byte[8192];
-               for (;;) {
-                       final int res = pIs.read(buffer);
-                       if (res == -1) {
-                               return baos.toByteArray();
-                       } else if (res > 0) {
-                               baos.write(buffer, 0, res);
-                       }
-               }
-       }
+    private static final Path testDir = 
Paths.get("target/unit-tests/DeferrableOutputStreamTest");
 
-       /**
-        * Tests using the {@link DeferrableOutputStream} with a positive 
threshold.
-        */
-       @Test
-       void testExceedPositiveThreshold() {
-               DeferrableOutputStream[] streams = new 
DeferrableOutputStream[1];
-               final Consumer<Consumer<OutputStream>> tester = (consumer) -> {
-                       try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(5, testFileSupplier, null)) {
-                               streams[0] = dos;
-                               assertTrue(dos.isInMemory());
-                               assertNull(dos.getPath());
-                               assertNull(dos.getBytes());
-                               assertSame(State.initialized, dos.getState());
-                               for (int i = 0;  i < 4;  i++) {
-                                       try {
-                                               dos.write('.');
-                                       } catch (IOException ioe) {
-                                               throw new 
UncheckedIOException(ioe);
-                                       }
-                                       assertSame(State.opened, 
dos.getState());
-                                       assertTrue(dos.isInMemory());
-                                       assertNull(dos.getPath());
-                                       assertNull(dos.getBytes());
-                               }
-                               consumer.accept(dos);
-                               assertFalse(dos.isInMemory());
-                               assertNotNull(dos.getPath());
-                               assertNull(dos.getBytes());
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
+    private static Path tempTestDir;
 
-                       final DeferrableOutputStream dos = streams[0];
-                       assertFalse(dos.isInMemory());
-                       assertNotNull(dos.getPath());
-                       assertTrue(Files.isRegularFile(dos.getPath()));
-                       final byte[] actual;
-                       try (InputStream is = dos.getInputStream()) {
-                               actual = read(is);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-                       final byte[] expect = 
"....,".getBytes(StandardCharsets.UTF_8);
-                       assertArrayEquals(expect, actual);
-               };
+    @BeforeAll
+    static void setUpTestDirs() throws IOException {
+        Files.createDirectories(testDir);
+        tempTestDir = Files.createTempDirectory(testDir, "testDir");
+    }
 
-               // Break the threshold using OutputStream.write(int);
-               tester.accept((os) -> {
-                       try {
-                               os.write(',');
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-               // Break the threshold using OutputStream.write(byte[]);
-               tester.accept((os) -> {
-                       final byte[] buffer = new byte[] {','};
-                       try {
-                               os.write(buffer);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-               // Break the threshold using OutputStream.write(byte[], int, 
int);
-               tester.accept((os) -> {
-                       final byte[] buffer = new byte[] {',', '-'};
-                       try {
-                               os.write(buffer, 0, 1);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-       }
+    private Supplier<Path> testFileSupplier = () -> {
+        try {
+            return Files.createTempFile(tempTestDir, "testFile", ".bin");
+        } catch (IOException ioe) {
+            throw new UncheckedIOException(ioe);
+        }
+    };
 
-       /**
-        * Tests using the {@link DeferrableOutputStream} with threshold -1.
-        */
-       @Test
-       void testThresholdMinusOne() {
-               DeferrableOutputStream[] streams = new 
DeferrableOutputStream[1];
-               final Runnable tester = () -> {
-                       try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(-1, testFileSupplier, null)) {
-                               streams[0] = dos;
-                               assertFalse(dos.isInMemory());
-                               assertNotNull(dos.getPath());
-                               assertNull(dos.getBytes());
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
+    protected byte[] read(InputStream pIs) throws IOException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final byte[] buffer = new byte[8192];
+        for (;;) {
+            final int res = pIs.read(buffer);
+            if (res == -1) {
+                return baos.toByteArray();
+            } else if (res > 0) {
+                baos.write(buffer, 0, res);
+            }
+        }
+    }
 
-                       final DeferrableOutputStream dos = streams[0];
-                       assertFalse(dos.isInMemory());
-                       assertNotNull(dos.getPath());
-                       assertTrue(Files.isRegularFile(dos.getPath()));
-                       final byte[] actual;
-                       try (InputStream is = dos.getInputStream()) {
-                               actual = read(is);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-                       final byte[] expect = 
"".getBytes(StandardCharsets.UTF_8);
-                       assertArrayEquals(expect, actual);
-               };
-               tester.run();
-       }
+    /**
+     * Tests using the {@link DeferrableOutputStream} with a positive 
threshold.
+     */
+    @Test
+    void testExceedPositiveThreshold() {
+        DeferrableOutputStream[] streams = new DeferrableOutputStream[1];
+        final Consumer<Consumer<OutputStream>> tester = (consumer) -> {
+            try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(5, testFileSupplier, null)) {
+                streams[0] = dos;
+                assertTrue(dos.isInMemory());
+                assertNull(dos.getPath());
+                assertNull(dos.getBytes());
+                assertSame(State.initialized, dos.getState());
+                for (int i = 0; i < 4; i++) {
+                    try {
+                        dos.write('.');
+                    } catch (IOException ioe) {
+                        throw new UncheckedIOException(ioe);
+                    }
+                    assertSame(State.opened, dos.getState());
+                    assertTrue(dos.isInMemory());
+                    assertNull(dos.getPath());
+                    assertNull(dos.getBytes());
+                }
+                consumer.accept(dos);
+                assertFalse(dos.isInMemory());
+                assertNotNull(dos.getPath());
+                assertNull(dos.getBytes());
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final DeferrableOutputStream dos = streams[0];
+            assertFalse(dos.isInMemory());
+            assertNotNull(dos.getPath());
+            assertTrue(Files.isRegularFile(dos.getPath()));
+            final byte[] actual;
+            try (InputStream is = dos.getInputStream()) {
+                actual = read(is);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final byte[] expect = "....,".getBytes(StandardCharsets.UTF_8);
+            assertArrayEquals(expect, actual);
+        };
+        // Break the threshold using OutputStream.write(int);
+        tester.accept((os) -> {
+            try {
+                os.write(',');
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+        // Break the threshold using OutputStream.write(byte[]);
+        tester.accept((os) -> {
+            final byte[] buffer = new byte[] { ',' };
+            try {
+                os.write(buffer);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+        // Break the threshold using OutputStream.write(byte[], int, int);
+        tester.accept((os) -> {
+            final byte[] buffer = new byte[] { ',', '-' };
+            try {
+                os.write(buffer, 0, 1);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+    }
 
-       /**
-        * Tests using the {@link DeferrableOutputStream} with threshold 0.
-        */
-       @Test
-       void testThresholdZero() {
-               DeferrableOutputStream[] streams = new 
DeferrableOutputStream[1];
-               final Consumer<Consumer<OutputStream>> tester = (consumer) -> {
-                       try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(0, testFileSupplier, null)) {
-                               streams[0] = dos;
-                               assertTrue(dos.isInMemory());
-                               assertNull(dos.getPath());
-                               assertNull(dos.getBytes());
-                               assertSame(State.initialized, dos.getState());
-                               consumer.accept(dos);
-                               assertFalse(dos.isInMemory());
-                               assertNotNull(dos.getPath());
-                               assertNull(dos.getBytes());
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
+    /**
+     * Tests that the temporary file is created with owner-only permissions on 
POSIX file systems, so that uploaded data in the shared system temporary
+     * directory is not readable by other users.
+     */
+    @Test
+    void testPersistedFileIsOwnerOnlyOnPosix() throws IOException {
+        
assumeTrue(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"));
+        // Mirror production, where the supplier only resolves a 
not-yet-existing path.
+        final Path target = tempTestDir.resolve("posixPerms.bin");
+        try (final DeferrableOutputStream dos = new DeferrableOutputStream(-1, 
() -> target, null)) {
+            dos.write("secret".getBytes(StandardCharsets.UTF_8));
+        }
+        assertTrue(Files.isRegularFile(target));
+        assertEquals("rw-------", 
PosixFilePermissions.toString(Files.getPosixFilePermissions(target)));
+    }
 
-                       final DeferrableOutputStream dos = streams[0];
-                       assertFalse(dos.isInMemory());
-                       assertNotNull(dos.getPath());
-                       assertTrue(Files.isRegularFile(dos.getPath()));
-                       final byte[] actual;
-                       try (InputStream is = dos.getInputStream()) {
-                               actual = read(is);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-                       final byte[] expect = 
",".getBytes(StandardCharsets.UTF_8);
-                       assertArrayEquals(expect, actual);
-               };
-               // Break the threshold using OutputStream.write(int);
-               tester.accept((os) -> {
-                       try {
-                               os.write(',');
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-               // Break the threshold using OutputStream.write(byte[]);
-               tester.accept((os) -> {
-                       final byte[] buffer = new byte[] {','};
-                       try {
-                               os.write(buffer);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-               // Break the threshold using OutputStream.write(byte[], int, 
int);
-               tester.accept((os) -> {
-                       final byte[] buffer = new byte[] {',', '-'};
-                       try {
-                               os.write(buffer, 0, 1);
-                       } catch (IOException ioe) {
-                               throw new UncheckedIOException(ioe);
-                       }
-               });
-       }
+    /**
+     * Tests that persisting over an already existing path truncates and 
overwrites it, matching the prior {@link Files#newOutputStream} behavior. Guards
+     * against a regression if {@code TRUNCATE_EXISTING} is dropped.
+     */
+    @Test
+    void testPersistedFileOverwritesExisting() throws IOException {
+        final Path target = tempTestDir.resolve("overwrite.bin");
+        Files.write(target, 
"stale-and-longer".getBytes(StandardCharsets.UTF_8));
+        try (final DeferrableOutputStream dos = new DeferrableOutputStream(-1, 
() -> target, null)) {
+            dos.write("new".getBytes(StandardCharsets.UTF_8));
+        }
+        assertTrue(Files.isRegularFile(target));
+        assertArrayEquals("new".getBytes(StandardCharsets.UTF_8), 
Files.readAllBytes(target));
+    }
 
-       /**
-        * Tests that the temporary file is created with owner-only permissions 
on POSIX file systems,
-        * so that uploaded data in the shared system temporary directory is 
not readable by other users.
-        */
-       @Test
-       void testPersistedFileIsOwnerOnlyOnPosix() throws IOException {
-               
assumeTrue(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"));
-               // Mirror production, where the supplier only resolves a 
not-yet-existing path.
-               final Path target = tempTestDir.resolve("posixPerms.bin");
-               try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(-1, () -> target, null)) {
-                       dos.write("secret".getBytes(StandardCharsets.UTF_8));
-               }
-               assertTrue(Files.isRegularFile(target));
-               assertEquals("rw-------", 
PosixFilePermissions.toString(Files.getPosixFilePermissions(target)));
-       }
+    /**
+     * Tests using the {@link DeferrableOutputStream} with threshold -1.
+     */
+    @Test
+    void testThresholdMinusOne() {
+        DeferrableOutputStream[] streams = new DeferrableOutputStream[1];
+        final Runnable tester = () -> {
+            try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(-1, testFileSupplier, null)) {
+                streams[0] = dos;
+                assertFalse(dos.isInMemory());
+                assertNotNull(dos.getPath());
+                assertNull(dos.getBytes());
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final DeferrableOutputStream dos = streams[0];
+            assertFalse(dos.isInMemory());
+            assertNotNull(dos.getPath());
+            assertTrue(Files.isRegularFile(dos.getPath()));
+            final byte[] actual;
+            try (InputStream is = dos.getInputStream()) {
+                actual = read(is);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final byte[] expect = "".getBytes(StandardCharsets.UTF_8);
+            assertArrayEquals(expect, actual);
+        };
+        tester.run();
+    }
 
-       /**
-        * Tests that persisting over an already existing path truncates and 
overwrites it, matching the prior
-        * {@link Files#newOutputStream} behavior. Guards against a regression 
if {@code TRUNCATE_EXISTING} is dropped.
-        */
-       @Test
-       void testPersistedFileOverwritesExisting() throws IOException {
-               final Path target = tempTestDir.resolve("overwrite.bin");
-               Files.write(target, 
"stale-and-longer".getBytes(StandardCharsets.UTF_8));
-               try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(-1, () -> target, null)) {
-                       dos.write("new".getBytes(StandardCharsets.UTF_8));
-               }
-               assertTrue(Files.isRegularFile(target));
-               assertArrayEquals("new".getBytes(StandardCharsets.UTF_8), 
Files.readAllBytes(target));
-       }
+    /**
+     * Tests using the {@link DeferrableOutputStream} with threshold 0.
+     */
+    @Test
+    void testThresholdZero() {
+        DeferrableOutputStream[] streams = new DeferrableOutputStream[1];
+        final Consumer<Consumer<OutputStream>> tester = (consumer) -> {
+            try (final DeferrableOutputStream dos = new 
DeferrableOutputStream(0, testFileSupplier, null)) {
+                streams[0] = dos;
+                assertTrue(dos.isInMemory());
+                assertNull(dos.getPath());
+                assertNull(dos.getBytes());
+                assertSame(State.initialized, dos.getState());
+                consumer.accept(dos);
+                assertFalse(dos.isInMemory());
+                assertNotNull(dos.getPath());
+                assertNull(dos.getBytes());
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final DeferrableOutputStream dos = streams[0];
+            assertFalse(dos.isInMemory());
+            assertNotNull(dos.getPath());
+            assertTrue(Files.isRegularFile(dos.getPath()));
+            final byte[] actual;
+            try (InputStream is = dos.getInputStream()) {
+                actual = read(is);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            final byte[] expect = ",".getBytes(StandardCharsets.UTF_8);
+            assertArrayEquals(expect, actual);
+        };
+        // Break the threshold using OutputStream.write(int);
+        tester.accept((os) -> {
+            try {
+                os.write(',');
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+        // Break the threshold using OutputStream.write(byte[]);
+        tester.accept((os) -> {
+            final byte[] buffer = new byte[] { ',' };
+            try {
+                os.write(buffer);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+        // Break the threshold using OutputStream.write(byte[], int, int);
+        tester.accept((os) -> {
+            final byte[] buffer = new byte[] { ',', '-' };
+            try {
+                os.write(buffer, 0, 1);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+        });
+    }
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 71634323..a080f290 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -46,7 +46,8 @@ The <action> type attribute can be add,update,fix,remove.
       <action                        type="fix" dev="ggregory" due-to="Henri 
Biestro, Gary Gregory">Fix migration documentation to mention Java 11.</action>
       <action issue="FILEUPLOAD-362" type="fix" dev="ggregory" due-to="Kusal 
Kithul-Godage, Gary Gregory">Unable to parse requests for file uploads with 
special characters in filename on Windows.</action>
       <action                        type="fix" dev="markt"    due-to="Mark 
Thomas">Implement stricter checks for fields using RFC 2231 / RFC 5987. Invalid 
extended values will be ignored.</action>
-      <action                        type="fix" dev="ggregory" due-to="Chenjp, 
Gary Gregory">RFC2231Utils decodeText - Incorrect handling of percent encoded 
text (#472).</action>
+      <action                        type="fix" dev="ggregory" due-to="Chenjp, 
Gary Gregory">RFC2231Utils decodeText: Incorrect handling of percent encoded 
text (#472).</action>
+      <action                        type="fix" dev="ggregory" due-to="alhuda, 
Gary Gregory">Create temporary upload file with owner-only permissions 
(#473).</action>
       <!-- ADD -->
       <!-- UPDATE -->
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
org.apache.commons:commons-parent from 96 to 102.</action>

Reply via email to