diff --git a/src/java.base/share/classes/java/nio/channels/AsynchronousFileChannel.java b/src/java.base/share/classes/java/nio/channels/AsynchronousFileChannel.java index 09054e30f86..2ad51f80a45 100644 --- a/src/java.base/share/classes/java/nio/channels/AsynchronousFileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/AsynchronousFileChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, 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 @@ -425,10 +425,13 @@ public abstract class AsynchronousFileChannel * required then a region starting at zero, and no smaller than the * expected maximum size of the file, should be locked. The two-argument * {@link #lock(Object,CompletionHandler)} method simply locks a region - * of size {@link Long#MAX_VALUE}. If a lock that overlaps the requested - * region is already held by this Java virtual machine, or this method has - * been invoked to lock an overlapping region and that operation has not - * completed, then this method throws {@link OverlappingFileLockException}. + * of size {@link Long#MAX_VALUE}. If the {@code position} is non-negative + * and the {@code size} is zero, then a lock of size + * {@code Long.MAX_VALUE - position} is returned. If a lock that + * overlaps the requested region is already held by this Java virtual + * machine, or this method has been invoked to lock an overlapping region + * and that operation has not completed, then this method throws + * {@link OverlappingFileLockException}. * *
Some operating systems do not support a mechanism to acquire a file * lock in an asynchronous manner. Consequently an implementation may @@ -454,7 +457,10 @@ public abstract class AsynchronousFileChannel * non-negative * @param size * The size of the locked region; must be non-negative, and the sum - * {@code position} + {@code size} must be non-negative + * {@code position} + {@code size} must be non-negative. + * A value of zero means to lock all bytes from the specified + * starting position to the end of the file, regardless of whether + * the file is subsequently extended or truncated * @param shared * {@code true} to request a shared lock, in which case this * channel must be open for reading (and possibly writing); @@ -532,7 +538,10 @@ public abstract class AsynchronousFileChannel * non-negative * @param size * The size of the locked region; must be non-negative, and the sum - * {@code position} + {@code size} must be non-negative + * {@code position} + {@code size} must be non-negative. + * A value of zero means to lock all bytes from the specified + * starting position to the end of the file, regardless of whether + * the file is subsequently extended or truncated * @param shared * {@code true} to request a shared lock, in which case this * channel must be open for reading (and possibly writing); @@ -586,7 +595,9 @@ public abstract class AsynchronousFileChannel * either having acquired a lock on the requested region or having failed to * do so. If it fails to acquire a lock because an overlapping lock is held * by another program then it returns {@code null}. If it fails to acquire - * a lock for any other reason then an appropriate exception is thrown. + * a lock for any other reason then an appropriate exception is thrown. If + * the {@code position} is non-negative and the {@code size} is zero, then a + * lock of size {@code Long.MAX_VALUE - position} is returned. * * @param position * The position at which the locked region is to start; must be @@ -594,7 +605,10 @@ public abstract class AsynchronousFileChannel * * @param size * The size of the locked region; must be non-negative, and the sum - * {@code position} + {@code size} must be non-negative + * {@code position} + {@code size} must be non-negative. + * A value of zero means to lock all bytes from the specified + * starting position to the end of the file, regardless of whether + * the file is subsequently extended or truncated * * @param shared * {@code true} to request a shared lock, diff --git a/src/java.base/share/classes/java/nio/channels/FileChannel.java b/src/java.base/share/classes/java/nio/channels/FileChannel.java index 9d9c35fbc1f..524e8e67cee 100644 --- a/src/java.base/share/classes/java/nio/channels/FileChannel.java +++ b/src/java.base/share/classes/java/nio/channels/FileChannel.java @@ -996,7 +996,9 @@ public abstract class FileChannel * required then a region starting at zero, and no smaller than the * expected maximum size of the file, should be locked. The zero-argument * {@link #lock()} method simply locks a region of size {@link - * Long#MAX_VALUE}. + * Long#MAX_VALUE}. If the {@code position} is non-negative and the + * {@code size} is zero, then a lock of size + * {@code Long.MAX_VALUE - position} is returned. * *
Some operating systems do not support shared locks, in which case a * request for a shared lock is automatically converted into a request for @@ -1014,7 +1016,10 @@ public abstract class FileChannel * * @param size * The size of the locked region; must be non-negative, and the sum - * {@code position} + {@code size} must be non-negative + * {@code position} + {@code size} must be non-negative. + * A value of zero means to lock all bytes from the specified + * starting position to the end of the file, regardless of whether + * the file is subsequently extended or truncated * * @param shared * {@code true} to request a shared lock, in which case this @@ -1123,7 +1128,9 @@ public abstract class FileChannel * required then a region starting at zero, and no smaller than the * expected maximum size of the file, should be locked. The zero-argument * {@link #tryLock()} method simply locks a region of size {@link - * Long#MAX_VALUE}. + * Long#MAX_VALUE}. If the {@code position} is non-negative and the + * {@code size} is zero, then a lock of size + * {@code Long.MAX_VALUE - position} is returned. * *
Some operating systems do not support shared locks, in which case a * request for a shared lock is automatically converted into a request for @@ -1141,7 +1148,10 @@ public abstract class FileChannel * * @param size * The size of the locked region; must be non-negative, and the sum - * {@code position} + {@code size} must be non-negative + * {@code position} + {@code size} must be non-negative. + * A value of zero means to lock all bytes from the specified + * starting position to the end of the file, regardless of whether + * the file is subsequently extended or truncated * * @param shared * {@code true} to request a shared lock, diff --git a/src/java.base/share/classes/java/nio/channels/FileLock.java b/src/java.base/share/classes/java/nio/channels/FileLock.java index 0edd94d5e25..3bd0b9c7b2c 100644 --- a/src/java.base/share/classes/java/nio/channels/FileLock.java +++ b/src/java.base/share/classes/java/nio/channels/FileLock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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 @@ -269,14 +269,36 @@ public abstract class FileLock implements AutoCloseable { * @param size * The size of the lock range * - * @return {@code true} if, and only if, this lock and the given lock - * range overlap by at least one byte + * @return {@code true} if this lock and the given lock range overlap + * by at least one byte; {@code false} if {@code size} is + * negative or the lock range does not overlap this lock */ public final boolean overlaps(long position, long size) { - if (position + size <= this.position) - return false; // That is below this - if (this.position + this.size <= position) - return false; // This is below that + if (size < 0) + return false; + + // Test whether this is below that + try { + if (Math.addExact(this.position, this.size) <= position) + return false; + } catch (ArithmeticException ignored) { + // the sum of this.position and this.size overflows the range of + // long hence their mathematical sum is greater than position + } + + // if size == 0 then the specified lock range is unbounded and + // cannot be below the range of this lock + if (size > 0) { + // Test whether that is below this + try { + if (Math.addExact(position, size) <= this.position) + return false; + } catch (ArithmeticException ignored) { + // the sum of position and size overflows the range of long + // hence their mathematical sum is greater than this.position + } + } + return true; } diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java index ec0bc767eb7..31a54a57a75 100644 --- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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 @@ -1271,6 +1271,8 @@ public class FileChannelImpl throw new NonReadableChannelException(); if (!shared && !writable) throw new NonWritableChannelException(); + if (size == 0) + size = Long.MAX_VALUE - Math.max(0, position); FileLockImpl fli = new FileLockImpl(this, position, size, shared); FileLockTable flt = fileLockTable(); flt.add(fli); @@ -1316,6 +1318,8 @@ public class FileChannelImpl throw new NonReadableChannelException(); if (!shared && !writable) throw new NonWritableChannelException(); + if (size == 0) + size = Long.MAX_VALUE - Math.max(0, position); FileLockImpl fli = new FileLockImpl(this, position, size, shared); FileLockTable flt = fileLockTable(); flt.add(fli); diff --git a/src/java.base/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java index 5658480558f..b41430c8d9d 100644 --- a/src/java.base/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2022, 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 @@ -181,8 +181,10 @@ public class SimpleAsynchronousFileChannelImpl if (!shared && !writing) throw new NonWritableChannelException(); + long len = (size != 0) ? size : Long.MAX_VALUE - Math.max(0, position); + // add to lock table - final FileLockImpl fli = addToFileLockTable(position, size, shared); + final FileLockImpl fli = addToFileLockTable(position, len, shared); if (fli == null) { Throwable exc = new ClosedChannelException(); if (handler == null) @@ -203,7 +205,7 @@ public class SimpleAsynchronousFileChannelImpl try { begin(); do { - n = nd.lock(fdObj, true, position, size, shared); + n = nd.lock(fdObj, true, position, len, shared); } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); if (n != FileDispatcher.LOCKED || !isOpen()) { throw new AsynchronousCloseException(); @@ -248,6 +250,9 @@ public class SimpleAsynchronousFileChannelImpl if (!shared && !writing) throw new NonWritableChannelException(); + if (size == 0) + size = Long.MAX_VALUE - Math.max(0, position); + // add to lock table FileLockImpl fli = addToFileLockTable(position, size, shared); if (fli == null) diff --git a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java index 21972975fa3..a1bacc1fae1 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2022, 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 @@ -299,8 +299,10 @@ public class WindowsAsynchronousFileChannelImpl if (!shared && !writing) throw new NonWritableChannelException(); + long len = (size != 0) ? size : Long.MAX_VALUE - Math.max(0, position); + // add to lock table - FileLockImpl fli = addToFileLockTable(position, size, shared); + FileLockImpl fli = addToFileLockTable(position, len, shared); if (fli == null) { Throwable exc = new ClosedChannelException(); if (handler == null) @@ -332,6 +334,9 @@ public class WindowsAsynchronousFileChannelImpl if (!shared && !writing) throw new NonWritableChannelException(); + if (size == 0) + size = Long.MAX_VALUE - Math.max(0, position); + // add to lock table final FileLockImpl fli = addToFileLockTable(position, size, shared); if (fli == null) diff --git a/test/jdk/java/nio/channels/AsynchronousFileChannel/Basic.java b/test/jdk/java/nio/channels/AsynchronousFileChannel/Basic.java index 55de744c0db..0beffac0ed5 100644 --- a/test/jdk/java/nio/channels/AsynchronousFileChannel/Basic.java +++ b/test/jdk/java/nio/channels/AsynchronousFileChannel/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2022, 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,18 +22,37 @@ */ /* @test - * @bug 4607272 6822643 6830721 6842687 + * @bug 4607272 5041655 6822643 6830721 6842687 * @summary Unit test for AsynchronousFileChannel * @key randomness */ -import java.nio.file.*; -import java.nio.channels.*; -import java.nio.ByteBuffer; import java.io.File; import java.io.IOException; -import java.util.*; -import java.util.concurrent.*; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.nio.channels.FileLock; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.OverlappingFileLockException; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeoutException;; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static java.nio.file.StandardOpenOption.*; @@ -176,7 +195,12 @@ public class Basic { // test 1 - acquire lock and check that tryLock throws // OverlappingFileLockException try { - fl = ch.lock().get(); + long pos = rand.nextInt(Integer.MAX_VALUE); + fl = ch.lock(pos, 0, false).get(); + long expectedSize = Long.MAX_VALUE - pos; + if(fl.size() != expectedSize) + throw new RuntimeException("Lock size " + fl.size() + + " != " + expectedSize + " for position " + pos); } catch (ExecutionException x) { throw new RuntimeException(x); } catch (InterruptedException x) { @@ -192,7 +216,12 @@ public class Basic { fl.release(); // test 2 - acquire try and check that lock throws OverlappingFileLockException - fl = ch.tryLock(); + long pos = rand.nextInt(Integer.MAX_VALUE); + fl = ch.tryLock(pos, 0, false); + long expectedSize = Long.MAX_VALUE - pos; + if(fl.size() != expectedSize) + throw new RuntimeException("Lock size " + fl.size() + " != " + + expectedSize + " for position " + pos); if (fl == null) throw new RuntimeException("Unable to acquire lock"); try { diff --git a/test/jdk/java/nio/channels/FileChannel/Lock.java b/test/jdk/java/nio/channels/FileChannel/Lock.java index 8dee478ea82..7a055a6d107 100644 --- a/test/jdk/java/nio/channels/FileChannel/Lock.java +++ b/test/jdk/java/nio/channels/FileChannel/Lock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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,12 +22,19 @@ */ /* @test - * @bug 4429043 4493595 6332756 6709457 7146506 + * @bug 4429043 4493595 5041655 6332756 6709457 7146506 * @summary Test FileChannel file locking */ -import java.io.*; -import java.nio.channels.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.Random; import static java.nio.file.StandardOpenOption.*; /** @@ -126,12 +133,23 @@ public class Lock { static void test2(File blah, boolean b) throws Exception { try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) { FileChannel channel = raf.getChannel(); - FileLock lock; - if (b) - lock = channel.lock(); - else - lock = channel.tryLock(); - lock.release(); + try (FileLock lock = b ? channel.lock() : channel.tryLock()) { + } + + Random rnd = new Random(System.currentTimeMillis()); + long position = rnd.nextInt(Integer.MAX_VALUE); + long expectedSize = Long.MAX_VALUE - position; + + for (boolean shared : new boolean[] {false, true}) { + try (FileLock lock = b ? channel.lock(position, 0, false) : + channel.tryLock(position, 0, false)) { + if(lock.size() != expectedSize) + throw new RuntimeException("Lock size " + lock.size() + + " != " + expectedSize + + " for position " + position + " of " + + (shared ? "exclusive" : "shared") + " lock"); + } + } } } diff --git a/test/jdk/java/nio/channels/FileLock/Overlaps.java b/test/jdk/java/nio/channels/FileLock/Overlaps.java new file mode 100644 index 00000000000..64293e2996d --- /dev/null +++ b/test/jdk/java/nio/channels/FileLock/Overlaps.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022, 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 5041655 + * @summary Verify FileLock.overlaps + * @run testng Overlaps + */ +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.file.Files; +import java.nio.file.Path; + +import static java.lang.Boolean.*; +import static java.nio.file.StandardOpenOption.*; + +import static org.testng.Assert.assertEquals; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class Overlaps { + private static final long POS = 27; + private static final long SIZE = 42; + + private static FileChannel fc; + + @BeforeClass + public void before() throws IOException { + Path path = Files.createTempFile(Path.of("."), "foo", ".bar"); + fc = FileChannel.open(path, CREATE, WRITE, DELETE_ON_CLOSE); + fc.position(POS); + fc.write(ByteBuffer.wrap(new byte[(int)SIZE])); + } + + @AfterClass + public void after() throws IOException { + fc.close(); + } + + @DataProvider + public Object[][] ranges() { + return new Object[][] { + {POS, SIZE, -1,-1, FALSE}, + {POS, SIZE, 0, -1, FALSE}, + {POS, SIZE, POS - 1, -1, FALSE}, + {POS, SIZE, POS + SIZE/2, -1, FALSE}, + {POS, SIZE, POS + SIZE, -1, FALSE}, + {POS, SIZE, -1, POS, FALSE}, + {POS, SIZE, -1, POS + SIZE/2, TRUE}, + {POS, SIZE, POS - 2, 1, FALSE}, + {POS, SIZE, POS + 1, 1, TRUE}, + {POS, SIZE, POS + SIZE/2, 0, TRUE}, + {POS, SIZE, Long.MAX_VALUE, 2, FALSE}, + {POS, SIZE, POS + SIZE / 2, Long.MAX_VALUE, TRUE}, + {POS, SIZE, 0, 0, TRUE}, + {Long.MAX_VALUE - SIZE/2, 0, 0, SIZE, FALSE}, + {Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE/4, SIZE, TRUE}, + {Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE, 0, TRUE}, + {Long.MAX_VALUE - SIZE, 0, Long.MAX_VALUE - SIZE/2, 0, TRUE} + }; + } + + @Test(dataProvider = "ranges") + public void overlaps(long lockPos, long lockSize, long pos, long size, + boolean overlaps) throws IOException { + try (FileLock lock = fc.lock(lockPos, lockSize, false)) { + assertEquals(lock.overlaps(pos, size), overlaps); + } + } +}