8204310: Simpler RandomAccessFile.setLength() on Windows
authorigerasim
Fri, 29 Jun 2018 17:35:04 -0700
changeset 50901 9f62267e79df
parent 50900 f651ae122ff7
child 50902 ec9957671c5d
8204310: Simpler RandomAccessFile.setLength() on Windows Reviewed-by: alanb
src/java.base/windows/native/libjava/io_util_md.c
src/java.base/windows/native/libjava/io_util_md.h
test/jdk/java/io/RandomAccessFile/SetLength.java
test/jdk/java/nio/channels/FileChannel/TruncateRAF.java
--- a/src/java.base/windows/native/libjava/io_util_md.c	Fri Jun 29 12:34:03 2018 -0700
+++ b/src/java.base/windows/native/libjava/io_util_md.c	Fri Jun 29 17:35:04 2018 -0700
@@ -458,19 +458,20 @@
     return 0;
 }
 
-
-int
+jint
 handleSetLength(FD fd, jlong length) {
     HANDLE h = (HANDLE)fd;
-    long high = (long)(length >> 32);
-    DWORD ret;
+    FILE_END_OF_FILE_INFO eofInfo;
 
-    if (h == (HANDLE)(-1)) return -1;
-    ret = SetFilePointer(h, (long)(length), &high, FILE_BEGIN);
-    if (ret == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
+    eofInfo.EndOfFile.QuadPart = length;
+
+    if (h == INVALID_HANDLE_VALUE) {
         return -1;
     }
-    if (SetEndOfFile(h) == FALSE) return -1;
+    if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &eofInfo,
+            sizeof(FILE_END_OF_FILE_INFO))) {
+        return -1;
+    }
     return 0;
 }
 
--- a/src/java.base/windows/native/libjava/io_util_md.h	Fri Jun 29 12:34:03 2018 -0700
+++ b/src/java.base/windows/native/libjava/io_util_md.h	Fri Jun 29 17:35:04 2018 -0700
@@ -43,7 +43,7 @@
 int currentDirLength(const WCHAR* path, int pathlen);
 int handleAvailable(FD fd, jlong *pbytes);
 int handleSync(FD fd);
-int handleSetLength(FD fd, jlong length);
+jint handleSetLength(FD fd, jlong length);
 jlong handleGetLength(FD fd);
 JNIEXPORT jint handleRead(FD fd, void *buf, jint len);
 jint handleWrite(FD fd, const void *buf, jint len);
--- a/test/jdk/java/io/RandomAccessFile/SetLength.java	Fri Jun 29 12:34:03 2018 -0700
+++ b/test/jdk/java/io/RandomAccessFile/SetLength.java	Fri Jun 29 17:35:04 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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,60 +22,143 @@
  */
 
 /* @test
-   @summary General tests of the setLength method -- Should migrate to 1.2 JCK
+ * @bug 8204310
+ * @summary General tests of the setLength method
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main SetLength
+ * @key randomness
  */
 
-import java.io.*;
+import java.io.IOException;
+import java.io.File;
+import java.io.RandomAccessFile;
 
+import jdk.test.lib.RandomFactory;
 
 public class SetLength {
 
-    static void fail(String s) {
-        throw new RuntimeException(s);
+    static void checkState(RandomAccessFile f, long expectedFilePointer,
+            long expectedLength)
+        throws IOException
+    {
+        long filePointer = f.getFilePointer();
+        long length = f.length();
+        if (length != expectedLength) {
+            throw new RuntimeException("File length " + length + " != expected "
+                    + expectedLength);
+        }
+        if (filePointer != expectedFilePointer) {
+            throw new RuntimeException("File pointer " + filePointer
+                    + " != expected " + expectedFilePointer);
+        }
     }
 
-    static void go(File fn, int max) throws IOException {
-        int chunk = max / 4;
-        long i;
-        RandomAccessFile f;
+    static void test(RandomAccessFile f, long quarterLength)
+        throws IOException
+    {
+        long halfLength = 2 * quarterLength;
+        long threeQuarterLength = 3 * quarterLength;
+        long fullLength = 4 * quarterLength;
+
+        // initially, empty file
+        checkState(f, 0, 0);
+
+        // extending the file size
+        f.setLength(halfLength);
+        checkState(f, 0, halfLength);
+
+        // writing from the begining
+        f.write(new byte[(int)fullLength]);
+        checkState(f, fullLength, fullLength);
+
+        // setting to the same length
+        f.setLength(fullLength);
+        checkState(f, fullLength, fullLength);
+
+        // truncating the file
+        f.setLength(threeQuarterLength);
+        checkState(f, threeQuarterLength, threeQuarterLength);
+
+        // changing the file pointer
+        f.seek(quarterLength);
+        checkState(f, quarterLength, threeQuarterLength);
+
+        // truncating the file again
+        f.setLength(halfLength);
+        checkState(f, quarterLength, halfLength);
+
+        // writing from the middle with extending the file
+        f.write(new byte[(int)halfLength]);
+        checkState(f, threeQuarterLength, threeQuarterLength);
+
+        // changing the file pointer
+        f.seek(quarterLength);
+        checkState(f, quarterLength, threeQuarterLength);
 
-        f = new RandomAccessFile(fn, "rw");
-        f.setLength(2 * chunk);
-        if (f.length() != 2 * chunk) fail("Length not increased to " + (2 * chunk));
-        if ((i = f.getFilePointer()) != 0) fail("File pointer shifted to " + i);
-        byte[] buf = new byte[max];
-        f.write(buf);
-        if (f.length() != max) fail("Write didn't work");
-        if (f.getFilePointer() != max) fail("File pointer inconsistent");
-        f.setLength(3 * chunk);
-        if (f.length() != 3 * chunk) fail("Length not reduced to " + 3 * chunk);
-        if (f.getFilePointer() != 3 * chunk) fail("File pointer not shifted to " + (3 * chunk));
-        f.seek(1 * chunk);
-        if (f.getFilePointer() != 1 * chunk) fail("File pointer not shifted to " + (1 * chunk));
-        f.setLength(2 * chunk);
-        if (f.length() != 2 * chunk) fail("Length not reduced to " + (2 * chunk));
-        if (f.getFilePointer() != 1 * chunk) fail("File pointer not shifted to " + (1 * chunk));
+        // writing from the middle without extending the file
+        f.write(new byte[(int)quarterLength]);
+        checkState(f, halfLength, threeQuarterLength);
+
+        // changing the file pointer to the end of file
+        f.seek(threeQuarterLength);
+        checkState(f, threeQuarterLength, threeQuarterLength);
+
+        // writing to the end of file
+        f.write(new byte[(int)quarterLength]);
+        checkState(f, fullLength, fullLength);
+
+        // truncating the file to zero
+        f.setLength(0);
+        checkState(f, 0, 0);
+
+        // changing the file pointer beyond the end of file
+        f.seek(threeQuarterLength);
+        checkState(f, threeQuarterLength, 0);
+
+        // writing beyont the end of file
+        f.write(new byte[(int)quarterLength]);
+        checkState(f, fullLength, fullLength);
+
+        // negative file pointer
+        try {
+            f.seek(-1);
+            throw new RuntimeException("IOE not thrown");
+        } catch (IOException expected) {
+        }
+        checkState(f, fullLength, fullLength);
+
+        // truncating the file after failed seek
+        f.setLength(halfLength);
+        checkState(f, halfLength, halfLength);
+
+        // truncating after closing
         f.close();
+        try {
+            f.setLength(halfLength);
+            throw new RuntimeException("IOE not thrown");
+        } catch (IOException expected) {
+        }
     }
 
     public static void main(String[] args) throws IOException {
-        File fn = new File("x.SetLength");
-        try {
-            go(fn, 20);
-            fn.delete();
-            go(fn, 64 * 1024);
-            RandomAccessFile f = new RandomAccessFile(fn, "r");
-            boolean thrown = false;
-            try {
-                f.setLength(3);
-            } catch (IOException x) {
-                thrown = true;
-            }
-            if (!thrown) fail("setLength succeeded on a file opened read-only");
-            f.close();
+        File f28b = new File("f28b");
+        File f28K = new File("f28K");
+        File frnd = new File("frnd");
+
+        try (RandomAccessFile raf28b = new RandomAccessFile(f28b, "rw");
+             RandomAccessFile raf28K = new RandomAccessFile(f28K, "rw");
+             RandomAccessFile rafrnd = new RandomAccessFile(frnd, "rw")) {
+            test(raf28b, 7);
+            test(raf28K, 7 * 1024);
+            test(rafrnd, 1 + RandomFactory.getRandom().nextInt(16000));
         }
-        finally {
-            fn.delete();
+
+        // truncating read-only file
+        try (RandomAccessFile raf28b = new RandomAccessFile(f28b, "r")) {
+            raf28b.setLength(42);
+            throw new RuntimeException("IOE not thrown");
+        } catch (IOException expected) {
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/FileChannel/TruncateRAF.java	Fri Jun 29 17:35:04 2018 -0700
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2018, 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 8204310
+ * @summary Check how FileChannel behaves if the file size/offset change via
+ *          RAF.setLength() and other methods.
+ * @run main TruncateRAF
+ */
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class TruncateRAF {
+
+    static void checkState(RandomAccessFile raf, FileChannel fch,
+            long expectedOffset, long expectedLength)
+        throws IOException
+    {
+        long rafLength = raf.length();
+        long rafOffset = raf.getFilePointer();
+        long fchLength = fch.size();
+        long fchOffset = fch.position();
+
+        if (rafLength != expectedLength)
+            throw new RuntimeException("rafLength (" + rafLength + ") != " +
+                    "expectedLength (" + expectedLength + ")");
+        if (rafOffset != expectedOffset)
+            throw new RuntimeException("rafOffset (" + rafOffset + ") != " +
+                    "expectedOffset (" + expectedOffset + ")");
+        if (fchLength != expectedLength)
+            throw new RuntimeException("fchLength (" + fchLength + ") != " +
+                    "expectedLength (" + expectedLength + ")");
+        if (fchOffset != expectedOffset)
+            throw new RuntimeException("fchOffset (" + fchOffset + ") != " +
+                    "expectedOffset (" + expectedOffset + ")");
+    }
+
+    public static void main(String[] args) throws Throwable {
+        File file = new File("tmp");
+        try (RandomAccessFile raf = new RandomAccessFile(file, "rw");
+             FileChannel fch = raf.getChannel()) {
+
+            // initially empty
+            checkState(raf, fch, 0, 0);
+
+            // seeking beyond EOF
+            raf.seek(42);
+            checkState(raf, fch, 42, 0);
+
+            // seeking beyond EOF
+            fch.position(84);
+            checkState(raf, fch, 84, 0);
+
+            // writing at offset beyond EOF
+            raf.write(1);
+            checkState(raf, fch, 85, 85);
+
+            // truncating
+            raf.setLength(63);
+            checkState(raf, fch, 63, 63);
+
+            // writing at EOF
+            fch.write(ByteBuffer.wrap(new byte[1]));
+            checkState(raf, fch, 64, 64);
+
+            // seeking at the middle
+            fch.position(32);
+            checkState(raf, fch, 32, 64);
+
+            // truncating beyond offset
+            fch.truncate(42);
+            checkState(raf, fch, 32, 42);
+
+            // truncating before offset
+            fch.truncate(16);
+            checkState(raf, fch, 16, 16);
+
+            // writing at position beyond EOF
+            fch.write(ByteBuffer.wrap(new byte[1]), 127);
+            checkState(raf, fch, 16, 128);
+
+            // writing at position before EOF
+            fch.write(ByteBuffer.wrap(new byte[1]), 42);
+            checkState(raf, fch, 16, 128);
+
+            // truncating
+            raf.setLength(64);
+            checkState(raf, fch, 16, 64);
+
+            // changing offset
+            raf.seek(21);
+            checkState(raf, fch, 21, 64);
+
+            // skipping should change offset
+            raf.skipBytes(4);
+            checkState(raf, fch, 25, 64);
+
+            // reading should change offset
+            raf.read();
+            checkState(raf, fch, 26, 64);
+
+            // truncating to zero
+            raf.setLength(0);
+            checkState(raf, fch, 0, 0);
+
+            // FileChannel cannot expand size
+            fch.truncate(42);
+            checkState(raf, fch, 0, 0);
+
+            // expanding
+            raf.setLength(42);
+            checkState(raf, fch, 0, 42);
+
+            // seeking beyond EOF
+            raf.seek(512);
+            checkState(raf, fch, 512, 42);
+
+            // truncating to the same size
+            fch.truncate(256);
+            checkState(raf, fch, 256, 42);
+
+            // truncating to the same size
+            fch.truncate(42);
+            checkState(raf, fch, 42, 42);
+
+            // truncating to zero
+            fch.truncate(0);
+            checkState(raf, fch, 0, 0);
+        }
+    }
+}