Skip to content

8136895: Writer not closed with disk full error, file resource leaked #3475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions src/java.base/share/classes/sun/nio/cs/StreamEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ void implFlush() throws IOException {
}

void implClose() throws IOException {
flushLeftoverChar(null, true);
try {
try (ch; out) {
flushLeftoverChar(null, true);
for (;;) {
CoderResult cr = encoder.flush(bb);
if (cr.isUnderflow())
Expand All @@ -338,15 +338,8 @@ void implClose() throws IOException {

if (bb.position() > 0)
writeBytes();
if (ch != null)
ch.close();
else {
try {
out.flush();
} finally {
out.close();
}
}
if (out != null)
out.flush();
} catch (IOException x) {
encoder.reset();
throw x;
Expand Down
86 changes: 86 additions & 0 deletions test/jdk/java/io/OutputStreamWriter/CloseWriterOnFailedFlush.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/* @test
* @bug 8136895
* @summary Verify stream closed after write error in StreamEncoder::implClose
*/

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;

public class CloseWriterOnFailedFlush {
private static final String STR_IOE = "Test"; // IOException
private static final String STR_MIE = "\ud83c"; // MalformedInputException

public static void main(String[] args) throws IOException {
boolean failed = false;

for (String s : new String[] {STR_IOE, STR_MIE}) {
System.out.println("string: " + s);
ErroringOutputStream stream = new ErroringOutputStream();
try (Writer writer = new OutputStreamWriter(stream,
StandardCharsets.UTF_8.newEncoder())) {
writer.write(s);
} catch (IOException ex) {
Class exClass = ex.getClass();
if (s.equals(STR_IOE) && exClass != IOException.class ||
s.equals(STR_MIE) && exClass != MalformedInputException.class)
throw ex;
}

if (stream.isOpen()) {
System.err.println("Stream is STILL open");
failed = true;
} else {
System.out.println("Stream is closed");
}
}

if (failed)
throw new RuntimeException("Test failed");
}

private static class ErroringOutputStream extends OutputStream {
private boolean open = true;

@Override
public void write(int b) throws IOException {
throw new IOException();
}

public boolean isOpen() {
return open;
}

@Override
public void close() throws IOException {
open = false;
System.out.println("Closing");
}
}
}
88 changes: 88 additions & 0 deletions test/jdk/java/nio/channels/Channels/CloseWriterOnFailedFlush.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/* @test
* @bug 8136895
* @summary Verify channel closed after write error in StreamEncoder::implClose
*/

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;

public class CloseWriterOnFailedFlush {
private static final String STR_IOE = "Test"; // IOException
private static final String STR_MIE = "\ud83c"; // MalformedInputException

public static void main(String[] args) throws IOException {
boolean failed = false;

for (String s : new String[] {STR_IOE, STR_MIE}) {
System.out.println("string: " + s);
ErroringByteChannel channel = new ErroringByteChannel();
try (Writer writer = Channels.newWriter
(channel, StandardCharsets.UTF_8.newEncoder(), -1 )) {
writer.write(s);
} catch (IOException ex) {
Class exClass = ex.getClass();
if (s.equals(STR_IOE) && exClass != IOException.class ||
s.equals(STR_MIE) && exClass != MalformedInputException.class)
throw ex;
}

if (channel.isOpen()) {
System.err.println("Channel is STILL open");
failed = true;
} else {
System.out.println("Channel is closed");
}
}

if (failed)
throw new RuntimeException("Test failed");
}

private static class ErroringByteChannel implements WritableByteChannel {
private boolean open = true;

@Override
public int write(ByteBuffer src) throws IOException {
throw new IOException();
}

@Override
public boolean isOpen() {
return open;
}

@Override
public void close() {
open = false;
System.out.println("Closing");
}
}
}
18 changes: 12 additions & 6 deletions test/jdk/sun/nio/cs/StreamEncoderClose.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -31,6 +31,15 @@

import java.io.*;
public class StreamEncoderClose {
private static void ck(String s, int actual, int expected)
throws IOException {
if (actual != expected) {
String msg = String.format("%s: actual (%d) != expected (%d)%n",
s, actual, expected);
throw new IOException(msg);
}
}

public static void main( String arg[] ) throws Exception {
byte[] expected = {(byte)0x1b,(byte)0x24,(byte)0x42,
(byte)0x30,(byte)0x6c,
Expand All @@ -46,13 +55,10 @@ public static void main( String arg[] ) throws Exception {

//double check, probably not necessary
byte[] out = baos.toByteArray();
if (out.length != expected.length) {
throw new IOException("Failed");
}
ck("Lengths are unequal", out.length, expected.length);
for (int i = 0; i < out.length; i++) {
//System.out.printf("(byte)0x%x,", out[i] & 0xff);
if (out[i] != expected[i])
throw new IOException("Failed");
ck("Values are unequal", out[i], expected[i]);
}
}

Expand Down