8023173: FileDescriptor should respect append flag
Reviewed-by: martin, alanb, rriggs
--- a/jdk/make/mapfiles/libjava/mapfile-vers Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/make/mapfiles/libjava/mapfile-vers Tue Oct 28 15:36:27 2014 +0300
@@ -75,6 +75,7 @@
Java_java_io_FileDescriptor_initIDs;
Java_java_io_FileDescriptor_sync;
+ Java_java_io_FileDescriptor_getAppend;
Java_java_io_FileInputStream_available;
Java_java_io_FileInputStream_close0;
Java_java_io_FileInputStream_initIDs;
--- a/jdk/src/java.base/share/classes/java/io/File.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/java/io/File.java Tue Oct 28 15:36:27 2014 +0300
@@ -1588,7 +1588,7 @@
/**
* A convenience method to set the owner's read permission for this abstract
* pathname. On some platforms it may be possible to start the Java virtual
- * machine with special privileges that allow it to read files that that are
+ * machine with special privileges that allow it to read files that are
* marked as unreadable.
*
* <p>An invocation of this method of the form <tt>file.setReadable(arg)</tt>
--- a/jdk/src/java.base/share/classes/java/io/FileOutputStream.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/java/io/FileOutputStream.java Tue Oct 28 15:36:27 2014 +0300
@@ -26,6 +26,8 @@
package java.io;
import java.nio.channels.FileChannel;
+import sun.misc.SharedSecrets;
+import sun.misc.JavaIOFileDescriptorAccess;
import sun.nio.ch.FileChannelImpl;
@@ -53,16 +55,17 @@
class FileOutputStream extends OutputStream
{
/**
+ * Access to FileDescriptor internals.
+ */
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
+ /**
* The system dependent file descriptor.
*/
private final FileDescriptor fd;
/**
- * True if the file is opened for append.
- */
- private final boolean append;
-
- /**
* The associated channel, initialized lazily.
*/
private FileChannel channel;
@@ -207,7 +210,6 @@
}
this.fd = new FileDescriptor();
fd.attach(this);
- this.append = append;
this.path = name;
open(name, append);
@@ -245,7 +247,6 @@
security.checkWrite(fdObj);
}
this.fd = fdObj;
- this.append = false;
this.path = null;
fd.attach(this);
@@ -287,7 +288,7 @@
* @exception IOException if an I/O error occurs.
*/
public void write(int b) throws IOException {
- write(b, append);
+ write(b, fdAccess.getAppend(fd));
}
/**
@@ -310,7 +311,7 @@
* @exception IOException if an I/O error occurs.
*/
public void write(byte b[]) throws IOException {
- writeBytes(b, 0, b.length, append);
+ writeBytes(b, 0, b.length, fdAccess.getAppend(fd));
}
/**
@@ -323,7 +324,7 @@
* @exception IOException if an I/O error occurs.
*/
public void write(byte b[], int off, int len) throws IOException {
- writeBytes(b, off, len, append);
+ writeBytes(b, off, len, fdAccess.getAppend(fd));
}
/**
@@ -395,7 +396,7 @@
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
- channel = FileChannelImpl.open(fd, path, false, true, append, this);
+ channel = FileChannelImpl.open(fd, path, false, true, this);
}
return channel;
}
--- a/jdk/src/java.base/share/classes/java/nio/file/FileStore.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/java/nio/file/FileStore.java Tue Oct 28 15:36:27 2014 +0300
@@ -208,7 +208,7 @@
* @param attribute
* the attribute to read
- * @return the attribute value; {@code null} may be a valid for some
+ * @return the attribute value; {@code null} may be valid for some
* attributes
*
* @throws UnsupportedOperationException
--- a/jdk/src/java.base/share/classes/java/nio/file/attribute/package-info.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/java/nio/file/attribute/package-info.java Tue Oct 28 15:36:27 2014 +0300
@@ -51,7 +51,7 @@
* <p> An attribute view provides a read-only or updatable view of the non-opaque
* values, or <em>metadata</em>, associated with objects in a file system.
* The {@link java.nio.file.attribute.FileAttributeView} interface is
- * extended by several other interfaces that views to specific sets of file
+ * extended by several other interfaces that provide views to specific sets of file
* attributes. {@code FileAttributeViews} are selected by invoking the {@link
* java.nio.file.Files#getFileAttributeView} method with a
* <em>type-token</em> to identify the required view. Views can also be identified
--- a/jdk/src/java.base/share/classes/sun/misc/JavaIOFileDescriptorAccess.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/misc/JavaIOFileDescriptorAccess.java Tue Oct 28 15:36:27 2014 +0300
@@ -33,6 +33,8 @@
public interface JavaIOFileDescriptorAccess {
public void set(FileDescriptor obj, int fd);
public int get(FileDescriptor fd);
+ public void setAppend(FileDescriptor obj, boolean append);
+ public boolean getAppend(FileDescriptor obj);
// Only valid on Windows
public void setHandle(FileDescriptor obj, long handle);
--- a/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Tue Oct 28 15:36:27 2014 +0300
@@ -44,6 +44,8 @@
import java.util.List;
import sun.misc.Cleaner;
+import sun.misc.JavaIOFileDescriptorAccess;
+import sun.misc.SharedSecrets;
import sun.security.action.GetPropertyAction;
public class FileChannelImpl
@@ -52,6 +54,10 @@
// Memory allocation size for mapping buffers
private static final long allocationGranularity;
+ // Access to FileDispatcher internals
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
// Used to make native read and write calls
private final FileDispatcher nd;
@@ -61,7 +67,6 @@
// File access mode (immutable)
private final boolean writable;
private final boolean readable;
- private final boolean append;
// Required to prevent finalization of creating stream (immutable)
private final Object parent;
@@ -77,31 +82,23 @@
private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, String path, boolean readable,
- boolean writable, boolean append, Object parent)
+ boolean writable, Object parent)
{
this.fd = fd;
this.readable = readable;
this.writable = writable;
- this.append = append;
this.parent = parent;
this.path = path;
- this.nd = new FileDispatcherImpl(append);
+ this.nd = new FileDispatcherImpl();
}
- // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
+ // Used by FileInputStream.getChannel(), FileOutputStream.getChannel
+ // and RandomAccessFile.getChannel()
public static FileChannel open(FileDescriptor fd, String path,
boolean readable, boolean writable,
Object parent)
{
- return new FileChannelImpl(fd, path, readable, writable, false, parent);
- }
-
- // Used by FileOutputStream.getChannel
- public static FileChannel open(FileDescriptor fd, String path,
- boolean readable, boolean writable,
- boolean append, Object parent)
- {
- return new FileChannelImpl(fd, path, readable, writable, append, parent);
+ return new FileChannelImpl(fd, path, readable, writable, parent);
}
private void ensureOpen() throws IOException {
@@ -109,7 +106,6 @@
throw new ClosedChannelException();
}
-
// -- Standard channel operations --
protected void implCloseChannel() throws IOException {
@@ -258,6 +254,7 @@
ti = threads.add();
if (!isOpen())
return 0;
+ boolean append = fdAccess.getAppend(fd);
do {
// in append-mode then position is advanced to end before writing
p = (append) ? nd.size(fd) : position0(fd, -1);
@@ -284,7 +281,7 @@
if (!isOpen())
return null;
do {
- p = position0(fd, newPosition);
+ p = position0(fd, newPosition);
} while ((p == IOStatus.INTERRUPTED) && isOpen());
return this;
} finally {
--- a/jdk/src/java.base/share/native/libjava/io_util.h Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/share/native/libjava/io_util.h Tue Oct 28 15:36:27 2014 +0300
@@ -28,6 +28,7 @@
extern jfieldID IO_fd_fdID;
extern jfieldID IO_handle_fdID;
+extern jfieldID IO_append_fdID;
#ifdef _ALLBSD_SOURCE
#include <fcntl.h>
--- a/jdk/src/java.base/unix/classes/java/io/FileDescriptor.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/unix/classes/java/io/FileDescriptor.java Tue Oct 28 15:36:27 2014 +0300
@@ -52,15 +52,21 @@
private boolean closed;
/**
+ * true, if file is opened for appending.
+ */
+ private boolean append;
+
+ /**
* Constructs an (invalid) FileDescriptor
* object.
*/
- public /**/ FileDescriptor() {
+ public FileDescriptor() {
fd = -1;
}
- private /* */ FileDescriptor(int fd) {
+ private FileDescriptor(int fd) {
this.fd = fd;
+ this.append = getAppend(fd);
}
/**
@@ -149,6 +155,14 @@
return obj.fd;
}
+ public void setAppend(FileDescriptor obj, boolean append) {
+ obj.append = append;
+ }
+
+ public boolean getAppend(FileDescriptor obj) {
+ return obj.append;
+ }
+
public void setHandle(FileDescriptor obj, long handle) {
throw new UnsupportedOperationException();
}
@@ -160,6 +174,11 @@
);
}
+ /**
+ * Returns true, if the file was opened for appending.
+ */
+ private static native boolean getAppend(int fd);
+
/*
* Package private methods to track referents.
* If multiple streams point to the same FileDescriptor, we cycle
--- a/jdk/src/java.base/unix/classes/sun/nio/ch/FileDispatcherImpl.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/unix/classes/sun/nio/ch/FileDispatcherImpl.java Tue Oct 28 15:36:27 2014 +0300
@@ -35,10 +35,6 @@
init();
}
- FileDispatcherImpl(boolean append) {
- /* append is ignored */
- }
-
FileDispatcherImpl() {
}
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixChannelFactory.java Tue Oct 28 15:36:27 2014 +0300
@@ -134,7 +134,7 @@
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
FileDescriptor fdObj = open(dfd, path, pathForPermissionCheck, flags, mode);
- return FileChannelImpl.open(fdObj, path.toString(), flags.read, flags.write, flags.append, null);
+ return FileChannelImpl.open(fdObj, path.toString(), flags.read, flags.write, null);
}
/**
@@ -288,6 +288,7 @@
// create java.io.FileDescriptor
FileDescriptor fdObj = new FileDescriptor();
fdAccess.set(fdObj, fd);
+ fdAccess.setAppend(fdObj, flags.append);
return fdObj;
}
}
--- a/jdk/src/java.base/unix/native/libjava/FileDescriptor_md.c Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/unix/native/libjava/FileDescriptor_md.c Tue Oct 28 15:36:27 2014 +0300
@@ -23,6 +23,9 @@
* questions.
*/
+#include <unistd.h>
+#include <fcntl.h>
+
#include "jvm.h"
#include "io_util_md.h"
@@ -35,6 +38,9 @@
/* field id for jint 'fd' in java.io.FileDescriptor */
jfieldID IO_fd_fdID;
+/* field id for jboolean 'append' in java.io.FileDescriptor */
+jfieldID IO_append_fdID;
+
/**************************************************************
* static methods to store field ID's in initializers
*/
@@ -42,6 +48,7 @@
JNIEXPORT void JNICALL
Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) {
IO_fd_fdID = (*env)->GetFieldID(env, fdClass, "fd", "I");
+ IO_append_fdID = (*env)->GetFieldID(env, fdClass, "append", "Z");
}
/**************************************************************
@@ -55,3 +62,9 @@
JNU_ThrowByName(env, "java/io/SyncFailedException", "sync failed");
}
}
+
+JNIEXPORT jboolean JNICALL
+Java_java_io_FileDescriptor_getAppend(JNIEnv *env, jclass fdClass, jint fd) {
+ int flags = fcntl(fd, F_GETFL);
+ return ((flags & O_APPEND) == 0) ? JNI_FALSE : JNI_TRUE;
+}
--- a/jdk/src/java.base/unix/native/libjava/io_util_md.c Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/unix/native/libjava/io_util_md.c Tue Oct 28 15:36:27 2014 +0300
@@ -107,7 +107,15 @@
#endif
fd = handleOpen(ps, flags, 0666);
if (fd != -1) {
+ jobject fdobj;
+ jboolean append;
SET_FD(this, fd, fid);
+
+ fdobj = (*env)->GetObjectField(env, this, fid);
+ if (fdobj != NULL) {
+ append = (flags & O_APPEND) == 0 ? JNI_FALSE : JNI_TRUE;
+ (*env)->SetBooleanField(env, fdobj, IO_append_fdID, append);
+ }
} else {
throwFileNotFoundException(env, path);
}
--- a/jdk/src/java.base/windows/classes/java/io/FileDescriptor.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/windows/classes/java/io/FileDescriptor.java Tue Oct 28 15:36:27 2014 +0300
@@ -51,6 +51,11 @@
private boolean closed;
/**
+ * true, if file is opened for appending.
+ */
+ private boolean append;
+
+ /**
* Constructs an (invalid) FileDescriptor
* object.
*/
@@ -75,6 +80,14 @@
return obj.fd;
}
+ public void setAppend(FileDescriptor obj, boolean append) {
+ obj.append = append;
+ }
+
+ public boolean getAppend(FileDescriptor obj) {
+ return obj.append;
+ }
+
public void setHandle(FileDescriptor obj, long handle) {
obj.handle = handle;
}
--- a/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Tue Oct 28 15:36:27 2014 +0300
@@ -31,22 +31,14 @@
class FileDispatcherImpl extends FileDispatcher
{
+ private static final JavaIOFileDescriptorAccess fdAccess =
+ SharedSecrets.getJavaIOFileDescriptorAccess();
+
static {
IOUtil.load();
}
- /**
- * Indicates if the dispatcher should first advance the file position
- * to the end of file when writing.
- */
- private final boolean append;
-
- FileDispatcherImpl(boolean append) {
- this.append = append;
- }
-
FileDispatcherImpl() {
- this(false);
}
@Override
@@ -71,7 +63,7 @@
}
int write(FileDescriptor fd, long address, int len) throws IOException {
- return write0(fd, address, len, append);
+ return write0(fd, address, len, fdAccess.getAppend(fd));
}
int pwrite(FileDescriptor fd, long address, int len, long position)
@@ -81,7 +73,7 @@
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
- return writev0(fd, address, len, append);
+ return writev0(fd, address, len, fdAccess.getAppend(fd));
}
int force(FileDescriptor fd, boolean metaData) throws IOException {
@@ -112,8 +104,6 @@
FileDescriptor duplicateForMapping(FileDescriptor fd) throws IOException {
// on Windows we need to keep a handle to the file
- JavaIOFileDescriptorAccess fdAccess =
- SharedSecrets.getJavaIOFileDescriptorAccess();
FileDescriptor result = new FileDescriptor();
long handle = duplicateHandle(fdAccess.getHandle(fd));
fdAccess.setHandle(result, handle);
--- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java Tue Oct 28 15:36:27 2014 +0300
@@ -160,7 +160,7 @@
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
- return FileChannelImpl.open(fdObj, pathForWindows, flags.read, flags.write, flags.append, null);
+ return FileChannelImpl.open(fdObj, pathForWindows, flags.read, flags.write, null);
}
/**
@@ -339,6 +339,7 @@
// create FileDescriptor and return
FileDescriptor fdObj = new FileDescriptor();
fdAccess.setHandle(fdObj, handle);
+ fdAccess.setAppend(fdObj, flags.append);
return fdObj;
}
}
--- a/jdk/src/java.base/windows/native/libjava/FileDescriptor_md.c Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/windows/native/libjava/FileDescriptor_md.c Tue Oct 28 15:36:27 2014 +0300
@@ -42,6 +42,9 @@
/* field id for jlong 'handle' in java.io.FileDescriptor */
jfieldID IO_handle_fdID;
+/* field id for jboolean 'append' in java.io.FileDescriptor */
+jfieldID IO_append_fdID;
+
/**************************************************************
* static methods to store field IDs in initializers
*/
@@ -50,6 +53,7 @@
Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) {
CHECK_NULL(IO_fd_fdID = (*env)->GetFieldID(env, fdClass, "fd", "I"));
CHECK_NULL(IO_handle_fdID = (*env)->GetFieldID(env, fdClass, "handle", "J"));
+ CHECK_NULL(IO_append_fdID = (*env)->GetFieldID(env, fdClass, "append", "Z"));
}
JNIEXPORT jlong JNICALL
--- a/jdk/src/java.base/windows/native/libjava/io_util_md.c Mon Oct 27 16:24:43 2014 -0700
+++ b/jdk/src/java.base/windows/native/libjava/io_util_md.c Tue Oct 28 15:36:27 2014 +0300
@@ -275,7 +275,15 @@
{
FD h = winFileHandleOpen(env, path, flags);
if (h >= 0) {
+ jobject fdobj;
+ jboolean append;
SET_FD(this, h, fid);
+
+ fdobj = (*env)->GetObjectField(env, this, fid);
+ if (fdobj != NULL) {
+ append = (flags & O_APPEND) == 0 ? JNI_FALSE : JNI_TRUE;
+ (*env)->SetBooleanField(env, fdobj, IO_append_fdID, append);
+ }
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/io/FileDescriptor/RememberAppend.java Tue Oct 28 15:36:27 2014 +0300
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, 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 8023173
+ * @summary FileDescriptor should respect append flag
+ */
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+
+public class RememberAppend {
+ private static final byte[] bytes = "ABC ".getBytes();
+
+ public static void main(String[] args) throws Throwable {
+ File f = File.createTempFile("tmp.file", null);
+ f.deleteOnExit();
+
+ try (FileOutputStream fos1 = new FileOutputStream(f.getPath(), true)) {
+ fos1.write(bytes);
+ }
+
+ try (FileOutputStream fos1 = new FileOutputStream(f.getPath(), true);
+ FileOutputStream fos2 = new FileOutputStream(fos1.getFD())) {
+ fos2.write(bytes);
+ }
+
+ if (f.length() != 2 * bytes.length) {
+ throw new RuntimeException("Append flag ignored");
+ }
+ }
+}