# HG changeset patch # User alanb # Date 1352038055 0 # Node ID 1f9a8607f4dfb3bef357d3d379d9b50aa0281366 # Parent 8435a30053c1d4040faecd4a99dd3b37d101a45c 8000330: (fc) FileChannel.truncate issues when given size > file size 8002180: (fc) FileChannel.map does not throw NPE if MapMode specified as null Reviewed-by: chegar diff -r 8435a30053c1 -r 1f9a8607f4df jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java --- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Fri Nov 02 15:50:11 2012 +0000 +++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Sun Nov 04 14:07:35 2012 +0000 @@ -302,12 +302,10 @@ } } - public FileChannel truncate(long size) throws IOException { + public FileChannel truncate(long newSize) throws IOException { ensureOpen(); - if (size < 0) - throw new IllegalArgumentException(); - if (size > size()) - return this; + if (newSize < 0) + throw new IllegalArgumentException("Negative size"); if (!writable) throw new NonWritableChannelException(); synchronized (positionLock) { @@ -320,6 +318,14 @@ if (!isOpen()) return null; + // get current size + long size; + do { + size = nd.size(fd); + } while ((size == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + // get current position do { p = position0(fd, -1); @@ -328,16 +334,18 @@ return null; assert p >= 0; - // truncate file - do { - rv = nd.truncate(fd, size); - } while ((rv == IOStatus.INTERRUPTED) && isOpen()); - if (!isOpen()) - return null; + // truncate file if given size is less than the current size + if (newSize < size) { + do { + rv = nd.truncate(fd, newSize); + } while ((rv == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + } - // set position to size if greater than size - if (p > size) - p = size; + // if position is beyond new size then adjust it + if (p > newSize) + p = newSize; do { rv = (int)position0(fd, p); } while ((rv == IOStatus.INTERRUPTED) && isOpen()); @@ -779,6 +787,8 @@ throws IOException { ensureOpen(); + if (mode == null) + throw new NullPointerException("Mode is null"); if (position < 0L) throw new IllegalArgumentException("Negative position"); if (size < 0L) @@ -787,6 +797,7 @@ throw new IllegalArgumentException("Position + size overflow"); if (size > Integer.MAX_VALUE) throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE"); + int imode = -1; if (mode == MapMode.READ_ONLY) imode = MAP_RO; diff -r 8435a30053c1 -r 1f9a8607f4df jdk/test/java/nio/channels/FileChannel/MapTest.java --- a/jdk/test/java/nio/channels/FileChannel/MapTest.java Fri Nov 02 15:50:11 2012 +0000 +++ b/jdk/test/java/nio/channels/FileChannel/MapTest.java Sun Nov 04 14:07:35 2012 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2012, 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 @@ -22,6 +22,7 @@ */ /* @test + * @bug 4429043 8002180 * @summary Test file mapping with FileChannel * @run main/othervm MapTest */ @@ -29,7 +30,10 @@ import java.io.*; import java.nio.MappedByteBuffer; import java.nio.channels.*; -import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.file.Files; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.charset.StandardCharsets.*; import java.util.Random; @@ -39,6 +43,7 @@ public class MapTest { + private static PrintStream out = System.out; private static PrintStream err = System.err; private static Random generator = new Random(); @@ -51,15 +56,21 @@ blah = File.createTempFile("blah", null); blah.deleteOnExit(); initTestFile(blah); - err.println("Test file " + blah + " initialized"); - testZero(); - err.println("Zero size: OK"); - testRead(); - err.println("Read: OK"); - testWrite(); - err.println("Write: OK"); - testHighOffset(); - err.println("High offset: OK"); + try { + out.println("Test file " + blah + " initialized"); + testZero(); + out.println("Zero size: OK"); + testRead(); + out.println("Read: OK"); + testWrite(); + out.println("Write: OK"); + testHighOffset(); + out.println("High offset: OK"); + testExceptions(); + out.println("Exceptions: OK"); + } finally { + blah.delete(); + } } /** @@ -77,30 +88,25 @@ * ability to index into a file of multiple pages is tested. */ private static void initTestFile(File blah) throws Exception { - FileOutputStream fos = new FileOutputStream(blah); - BufferedWriter awriter - = new BufferedWriter(new OutputStreamWriter(fos, "8859_1")); - - for(int i=0; i<4000; i++) { - String number = new Integer(i).toString(); - for (int h=0; h<4-number.length(); h++) - awriter.write("0"); - awriter.write(""+i); - awriter.newLine(); + try (BufferedWriter writer = Files.newBufferedWriter(blah.toPath(), ISO_8859_1)) { + for (int i=0; i<4000; i++) { + String number = new Integer(i).toString(); + for (int h=0; h<4-number.length(); h++) + writer.write("0"); + writer.write(""+i); + writer.newLine(); + } } - awriter.flush(); - awriter.close(); } /** * Tests zero size file mapping */ private static void testZero() throws Exception { - FileInputStream fis = new FileInputStream(blah); - FileChannel c = fis.getChannel(); - MappedByteBuffer b = c.map(FileChannel.MapMode.READ_ONLY, 0, 0); - c.close(); - fis.close(); + try (FileInputStream fis = new FileInputStream(blah)) { + FileChannel fc = fis.getChannel(); + MappedByteBuffer b = fc.map(MapMode.READ_ONLY, 0, 0); + } } /** @@ -108,33 +114,32 @@ * from the ByteBuffer gets the right line number */ private static void testRead() throws Exception { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.setLength(4); for (int x=0; x<1000; x++) { - FileInputStream fis = new FileInputStream(blah); - FileChannel c = fis.getChannel(); + try (FileInputStream fis = new FileInputStream(blah)) { + FileChannel fc = fis.getChannel(); - long offset = generator.nextInt(10000); - long expectedResult = offset / CHARS_PER_LINE; - offset = expectedResult * CHARS_PER_LINE; + long offset = generator.nextInt(10000); + long expectedResult = offset / CHARS_PER_LINE; + offset = expectedResult * CHARS_PER_LINE; - MappedByteBuffer b = c.map(FileChannel.MapMode.READ_ONLY, - offset, 100); + MappedByteBuffer b = fc.map(MapMode.READ_ONLY, + offset, 100); - for (int i=0; i<4; i++) { - byte aByte = b.get(i); - sb.setCharAt(i, (char)aByte); - } + for (int i=0; i<4; i++) { + byte aByte = b.get(i); + sb.setCharAt(i, (char)aByte); + } - int result = Integer.parseInt(sb.toString()); - if (result != expectedResult) { - err.println("I expected "+expectedResult); - err.println("I got "+result); - throw new Exception("Read test failed"); + int result = Integer.parseInt(sb.toString()); + if (result != expectedResult) { + err.println("I expected "+expectedResult); + err.println("I got "+result); + throw new Exception("Read test failed"); + } } - c.close(); - fis.close(); } } @@ -143,46 +148,159 @@ * written out to the file can be read back in */ private static void testWrite() throws Exception { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.setLength(4); for (int x=0; x<1000; x++) { - RandomAccessFile raf = new RandomAccessFile(blah, "rw"); - FileChannel c = raf.getChannel(); + try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) { + FileChannel fc = raf.getChannel(); + + long offset = generator.nextInt(1000); + MappedByteBuffer b = fc.map(MapMode.READ_WRITE, + offset, 100); - long offset = generator.nextInt(1000); - MappedByteBuffer b = c.map(FileChannel.MapMode.READ_WRITE, - offset, 100); + for (int i=0; i<4; i++) { + b.put(i, (byte)('0' + i)); + } - for (int i=0; i<4; i++) { - b.put(i, (byte)('0' + i)); + for (int i=0; i<4; i++) { + byte aByte = b.get(i); + sb.setCharAt(i, (char)aByte); + } + if (!sb.toString().equals("0123")) + throw new Exception("Write test failed"); } - - for (int i=0; i<4; i++) { - byte aByte = b.get(i); - sb.setCharAt(i, (char)aByte); - } - if (!sb.toString().equals("0123")) - throw new Exception("Write test failed"); - c.close(); - raf.close(); } } private static void testHighOffset() throws Exception { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.setLength(4); for (int x=0; x<1000; x++) { - RandomAccessFile raf = new RandomAccessFile(blah, "rw"); - FileChannel fc = raf.getChannel(); - long offset = 66000; - MappedByteBuffer b = fc.map(FileChannel.MapMode.READ_WRITE, - offset, 100); + try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) { + FileChannel fc = raf.getChannel(); + long offset = 66000; + MappedByteBuffer b = fc.map(MapMode.READ_WRITE, + offset, 100); + } + } + } + + /** + * Test exceptions specified by map method + */ + private static void testExceptions() throws Exception { + // check exceptions when channel opened for read access + try (FileChannel fc = FileChannel.open(blah.toPath(), READ)) { + testExceptions(fc); + + checkException(fc, MapMode.READ_WRITE, 0L, fc.size(), + NonWritableChannelException.class); + + checkException(fc, MapMode.READ_WRITE, -1L, fc.size(), + NonWritableChannelException.class, IllegalArgumentException.class); + + checkException(fc, MapMode.READ_WRITE, 0L, -1L, + NonWritableChannelException.class, IllegalArgumentException.class); + + checkException(fc, MapMode.PRIVATE, 0L, fc.size(), + NonWritableChannelException.class); - fc.close(); - raf.close(); + checkException(fc, MapMode.PRIVATE, -1L, fc.size(), + NonWritableChannelException.class, IllegalArgumentException.class); + + checkException(fc, MapMode.PRIVATE, 0L, -1L, + NonWritableChannelException.class, IllegalArgumentException.class); + } + + // check exceptions when channel opened for write access + try (FileChannel fc = FileChannel.open(blah.toPath(), WRITE)) { + testExceptions(fc); + + checkException(fc, MapMode.READ_ONLY, 0L, fc.size(), + NonReadableChannelException.class); + + checkException(fc, MapMode.READ_ONLY, -1L, fc.size(), + NonReadableChannelException.class, IllegalArgumentException.class); + + /* + * implementation/spec mismatch, these tests disabled for now + */ + //checkException(fc, MapMode.READ_WRITE, 0L, fc.size(), + // NonWritableChannelException.class); + //checkException(fc, MapMode.PRIVATE, 0L, fc.size(), + // NonWritableChannelException.class); + } + + // check exceptions when channel opened for read and write access + try (FileChannel fc = FileChannel.open(blah.toPath(), READ, WRITE)) { + testExceptions(fc); } } + private static void testExceptions(FileChannel fc) throws IOException { + checkException(fc, null, 0L, fc.size(), + NullPointerException.class); + + checkException(fc, MapMode.READ_ONLY, -1L, fc.size(), + IllegalArgumentException.class); + + checkException(fc, null, -1L, fc.size(), + IllegalArgumentException.class, NullPointerException.class); + + checkException(fc, MapMode.READ_ONLY, 0L, -1L, + IllegalArgumentException.class); + + checkException(fc, null, 0L, -1L, + IllegalArgumentException.class, NullPointerException.class); + + checkException(fc, MapMode.READ_ONLY, 0L, Integer.MAX_VALUE + 1L, + IllegalArgumentException.class); + + checkException(fc, null, 0L, Integer.MAX_VALUE + 1L, + IllegalArgumentException.class, NullPointerException.class); + + checkException(fc, MapMode.READ_ONLY, Long.MAX_VALUE, 1L, + IllegalArgumentException.class); + + checkException(fc, null, Long.MAX_VALUE, 1L, + IllegalArgumentException.class, NullPointerException.class); + + } + + /** + * Checks that FileChannel map throws one of the expected exceptions + * when invoked with the given inputs. + */ + private static void checkException(FileChannel fc, + MapMode mode, + long position, + long size, + Class... expected) + throws IOException + { + Exception exc = null; + try { + fc.map(mode, position, size); + } catch (Exception actual) { + exc = actual; + } + if (exc != null) { + for (Class clazz: expected) { + if (clazz.isInstance(exc)) { + return; + } + } + } + System.err.println("Expected one of"); + for (Class clazz: expected) { + System.out.println(clazz); + } + if (exc == null) { + throw new RuntimeException("No expection thrown"); + } else { + throw new RuntimeException("Unexpected exception thrown", exc); + } + } } diff -r 8435a30053c1 -r 1f9a8607f4df jdk/test/java/nio/channels/FileChannel/Truncate.java --- a/jdk/test/java/nio/channels/FileChannel/Truncate.java Fri Nov 02 15:50:11 2012 +0000 +++ b/jdk/test/java/nio/channels/FileChannel/Truncate.java Sun Nov 04 14:07:35 2012 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2012, 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 @@ -22,14 +22,16 @@ */ /* @test - * @bug 6191269 6709457 + * @bug 6191269 6709457 8000330 * @summary Test truncate method of FileChannel */ import java.io.*; import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; +import java.nio.channels.*; +import java.nio.file.Files; import static java.nio.file.StandardOpenOption.*; +import static java.nio.charset.StandardCharsets.*; import java.util.Random; @@ -46,6 +48,7 @@ try { basicTest(blah); appendTest(blah); + exceptionTests(blah); } finally { blah.delete(); } @@ -66,15 +69,22 @@ if (fc.size() != testSize) throw new RuntimeException("Size failed"); - long position = generator.nextInt((int)testSize); + long position = generator.nextInt((int)testSize*2); fc.position(position); - long newSize = generator.nextInt((int)testSize); + long newSize = generator.nextInt((int)testSize*2); fc.truncate(newSize); - if (fc.size() != newSize) - throw new RuntimeException("Truncate failed"); + // check new size + if (newSize > testSize) { + if (fc.size() != testSize) + throw new RuntimeException("Attempt to expand file changed size"); + } else { + if (fc.size() != newSize) + throw new RuntimeException("Unexpected size after truncate"); + } + // check new position if (position > newSize) { if (fc.position() != newSize) throw new RuntimeException("Position greater than size"); @@ -115,20 +125,90 @@ } /** + * Test exceptions specified by truncate method + */ + static void exceptionTests(File blah) throws Exception { + // check exceptions when channel opened for read access + try (FileChannel fc = FileChannel.open(blah.toPath(), READ)) { + long size = fc.size(); + + // open channel + checkException(fc, 0L, NonWritableChannelException.class); + + checkException(fc, -1L, NonWritableChannelException.class, + IllegalArgumentException.class); + + checkException(fc, size+1L, NonWritableChannelException.class); + + // closed channel + fc.close(); + + checkException(fc, 0L, ClosedChannelException.class); + + checkException(fc, -1L, ClosedChannelException.class, + IllegalArgumentException.class); + + checkException(fc, size+1L, ClosedChannelException.class); + } + + // check exceptions when channel opened for write access + try (FileChannel fc = FileChannel.open(blah.toPath(), WRITE)) { + long size = fc.size(); + + // open channel + checkException(fc, -1L, IllegalArgumentException.class); + + // closed channel + fc.close(); + + checkException(fc, 0L, ClosedChannelException.class); + + checkException(fc, -1L, ClosedChannelException.class, + IllegalArgumentException.class); + + checkException(fc, size+1L, ClosedChannelException.class); + } + } + + /** + * Checks that FileChannel truncate throws one of the expected exceptions + * when invoked with the given size. + */ + private static void checkException(FileChannel fc, long size, Class... expected) + throws IOException + { + Exception exc = null; + try { + fc.truncate(size); + } catch (Exception actual) { + exc = actual; + } + if (exc != null) { + for (Class clazz: expected) { + if (clazz.isInstance(exc)) { + return; + } + } + } + System.err.println("Expected one of"); + for (Class clazz: expected) { + System.err.println(clazz); + } + if (exc == null) { + throw new RuntimeException("No expection thrown"); + } else { + throw new RuntimeException("Unexpected exception thrown", exc); + } + } + + /** * Creates file blah of specified size in bytes. - * */ private static void initTestFile(File blah, long size) throws Exception { - if (blah.exists()) - blah.delete(); - FileOutputStream fos = new FileOutputStream(blah); - BufferedWriter awriter - = new BufferedWriter(new OutputStreamWriter(fos, "8859_1")); - - for(int i=0; i