Skip to content

Commit 428d516

Browse files
author
Brian Burkhalter
committed
8140241: (fc) Data transfer from FileChannel to itself causes hang in case of overlap
Reviewed-by: alanb
1 parent 93cab7d commit 428d516

6 files changed

Lines changed: 167 additions & 7 deletions

File tree

src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,15 @@ private long transferToTrustedChannel(long position, long count,
585585
if (!((target instanceof FileChannelImpl) || isSelChImpl))
586586
return IOStatus.UNSUPPORTED;
587587

588+
if (target == this) {
589+
long posThis = position();
590+
if (posThis - count + 1 <= position &&
591+
position - count + 1 <= posThis &&
592+
!nd.canTransferToFromOverlappedMap()) {
593+
return IOStatus.UNSUPPORTED_CASE;
594+
}
595+
}
596+
588597
// Trusted target: Use a mapped buffer
589598
long remaining = count;
590599
while (remaining > 0L) {
@@ -677,7 +686,7 @@ public long transferTo(long position, long count,
677686
return 0;
678687

679688
if ((sz - position) < count)
680-
count = (int)(sz - position);
689+
count = sz - position;
681690

682691
// Attempt a direct transfer, if the kernel supports it, limiting
683692
// the number of bytes according to which platform
@@ -704,6 +713,14 @@ private long transferFromFileChannel(FileChannelImpl src,
704713
long pos = src.position();
705714
long max = Math.min(count, src.size() - pos);
706715

716+
if (src == this) {
717+
if (position() - max + 1 <= pos &&
718+
pos - max + 1 <= position() &&
719+
!nd.canTransferToFromOverlappedMap()) {
720+
return IOStatus.UNSUPPORTED_CASE;
721+
}
722+
}
723+
707724
long remaining = max;
708725
long p = pos;
709726
while (remaining > 0L) {
@@ -779,9 +796,12 @@ public long transferFrom(ReadableByteChannel src,
779796
throw new IllegalArgumentException();
780797
if (position > size())
781798
return 0;
782-
if (src instanceof FileChannelImpl)
783-
return transferFromFileChannel((FileChannelImpl)src,
784-
position, count);
799+
800+
if (src instanceof FileChannelImpl fci) {
801+
long n = transferFromFileChannel(fci, position, count);
802+
if (n >= 0)
803+
return n;
804+
}
785805

786806
return transferFromArbitraryChannel(src, position, count);
787807
}

src/java.base/share/classes/sun/nio/ch/FileDispatcher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -67,5 +67,7 @@ abstract FileDescriptor duplicateForMapping(FileDescriptor fd)
6767

6868
abstract boolean transferToDirectlyNeedsPositionLock();
6969

70+
abstract boolean canTransferToFromOverlappedMap();
71+
7072
abstract int setDirectIO(FileDescriptor fd, String path);
7173
}

src/java.base/unix/classes/sun/nio/ch/FileDispatcherImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -126,6 +126,10 @@ boolean transferToDirectlyNeedsPositionLock() {
126126
return false;
127127
}
128128

129+
boolean canTransferToFromOverlappedMap() {
130+
return canTransferToFromOverlappedMap0();
131+
}
132+
129133
int setDirectIO(FileDescriptor fd, String path) {
130134
int result = -1;
131135
try {
@@ -184,6 +188,8 @@ static native void release0(FileDescriptor fd, long pos, long size)
184188

185189
static native void closeIntFD(int fd) throws IOException;
186190

191+
static native boolean canTransferToFromOverlappedMap0();
192+
187193
static native int setDirect0(FileDescriptor fd) throws IOException;
188194

189195
static native void init();

src/java.base/unix/native/libnio/ch/FileDispatcherImpl.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ Java_sun_nio_ch_FileDispatcherImpl_closeIntFD(JNIEnv *env, jclass clazz, jint fd
338338
closeFileDescriptor(env, fd);
339339
}
340340

341+
JNIEXPORT jboolean JNICALL
342+
Java_sun_nio_ch_FileDispatcherImpl_canTransferToFromOverlappedMap0(JNIEnv *env, jclass clazz)
343+
{
344+
#ifdef MACOSX
345+
return JNI_FALSE;
346+
#else
347+
return JNI_TRUE;
348+
#endif
349+
}
350+
341351
JNIEXPORT jint JNICALL
342352
Java_sun_nio_ch_FileDispatcherImpl_setDirect0(JNIEnv *env, jclass clazz,
343353
jobject fdo)

src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -125,6 +125,10 @@ boolean transferToDirectlyNeedsPositionLock() {
125125
return true;
126126
}
127127

128+
boolean canTransferToFromOverlappedMap() {
129+
return true;
130+
}
131+
128132
int setDirectIO(FileDescriptor fd, String path) {
129133
int result = -1;
130134
String filePath = path.substring(0, path.lastIndexOf(File.separator));
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @bug 8140241
26+
* @summary Test transferring to and from same file channel
27+
*/
28+
import java.io.BufferedOutputStream;
29+
import java.io.File;
30+
import java.io.FileOutputStream;
31+
import java.io.OutputStream;
32+
import java.io.RandomAccessFile;
33+
import java.io.IOException;
34+
import java.nio.channels.FileChannel;
35+
import java.util.Random;
36+
37+
public class TransferOverlappedFileChannel {
38+
39+
public static void main(String[] args) throws Exception {
40+
File file = File.createTempFile("readingin", null);
41+
file.deleteOnExit();
42+
generateBigFile(file);
43+
RandomAccessFile raf = new RandomAccessFile(file, "rw");
44+
try (FileChannel channel = raf.getChannel()) {
45+
transferToNoOverlap(file, channel);
46+
transferToOverlap(file, channel);
47+
transferFromNoOverlap(file, channel);
48+
transferFromOverlap(file, channel);
49+
} finally {
50+
file.delete();
51+
}
52+
}
53+
54+
private static void transferToNoOverlap(File file, FileChannel channel)
55+
throws IOException {
56+
final long length = file.length();
57+
58+
// position at three quarters
59+
channel.position(length*3/4);
60+
// copy last quarter to third quarter
61+
// (copied and overwritten regions do NOT overlap)
62+
// So: 1 2 3 4 -> 1 2 4 4
63+
channel.transferTo(length / 2, length / 4, channel);
64+
System.out.println("transferToNoOverlap: OK");
65+
}
66+
67+
private static void transferToOverlap(File file, FileChannel channel)
68+
throws IOException {
69+
final long length = file.length();
70+
71+
// position at half
72+
channel.position(length/2);
73+
// copy last half to second quarter
74+
// (copied and overwritten regions DO overlap)
75+
// So: 1 2 3 4 -> 1 3 4 4
76+
channel.transferTo(length / 4, length / 2, channel);
77+
System.out.println("transferToOverlap: OK");
78+
}
79+
80+
private static void transferFromNoOverlap(File file, FileChannel channel)
81+
throws IOException {
82+
final long length = file.length();
83+
84+
// position at three quarters
85+
channel.position(length*3/4);
86+
// copy last quarter to third quarter
87+
// (copied and overwritten regions do NOT overlap)
88+
// So: 1 2 3 4 -> 1 2 4 4
89+
channel.transferFrom(channel, length / 2, length / 4);
90+
System.out.println("transferFromNoOverlap: OK");
91+
}
92+
93+
private static void transferFromOverlap(File file, FileChannel channel)
94+
throws IOException {
95+
final long length = file.length();
96+
97+
// position at half
98+
channel.position(length/2);
99+
// copy last half to second quarter
100+
// (copied and overwritten regions DO overlap)
101+
// So: 1 2 3 4 -> 1 3 4 4
102+
channel.transferFrom(channel, length / 4, length / 2);
103+
System.out.println("transferFromOverlap: OK");
104+
}
105+
106+
private static void generateBigFile(File file) throws Exception {
107+
try (OutputStream out = new BufferedOutputStream(
108+
new FileOutputStream(file))) {
109+
byte[] randomBytes = new byte[1024];
110+
Random rand = new Random(0);
111+
rand.nextBytes(randomBytes);
112+
for (int i = 0; i < 1024; i++) {
113+
out.write(randomBytes);
114+
}
115+
out.flush();
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)