diff --git a/jdk/src/share/classes/java/io/FileOutputStream.java b/jdk/src/share/classes/java/io/FileOutputStream.java
index 71bdee89f3b..70f6851d732 100644
--- a/jdk/src/share/classes/java/io/FileOutputStream.java
+++ b/jdk/src/share/classes/java/io/FileOutputStream.java
@@ -56,7 +56,15 @@ class FileOutputStream extends OutputStream
*/
private final FileDescriptor fd;
- private FileChannel channel= null;
+ /**
+ * True if the file is opened for append.
+ */
+ private final boolean append;
+
+ /**
+ * The associated channel, initalized lazily.
+ */
+ private FileChannel channel;
private final Object closeLock = new Object();
private volatile boolean closed = false;
@@ -196,7 +204,9 @@ class FileOutputStream extends OutputStream
if (name == null) {
throw new NullPointerException();
}
- fd = new FileDescriptor();
+ this.fd = new FileDescriptor();
+ this.append = append;
+
fd.incrementAndGetUseCount();
open(name, append);
}
@@ -232,7 +242,8 @@ class FileOutputStream extends OutputStream
if (security != null) {
security.checkWrite(fdObj);
}
- fd = fdObj;
+ this.fd = fdObj;
+ this.append = false;
/*
* FileDescriptor is being shared by streams.
@@ -250,6 +261,15 @@ class FileOutputStream extends OutputStream
private native void open(String name, boolean append)
throws FileNotFoundException;
+ /**
+ * Writes the specified byte to this file output stream.
+ *
+ * @param b the byte to be written.
+ * @param append {@code true} if the write operation first
+ * advances the position to the end of file
+ */
+ private native void write(int b, boolean append) throws IOException;
+
/**
* Writes the specified byte to this file output stream. Implements
* the write method of OutputStream.
@@ -257,16 +277,21 @@ class FileOutputStream extends OutputStream
* @param b the byte to be written.
* @exception IOException if an I/O error occurs.
*/
- public native void write(int b) throws IOException;
+ public void write(int b) throws IOException {
+ write(b, append);
+ }
/**
* Writes a sub array as a sequence of bytes.
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
+ * @param append {@code true} to first advance the position to the
+ * end of file
* @exception IOException If an I/O error has occurred.
*/
- private native void writeBytes(byte b[], int off, int len) throws IOException;
+ private native void writeBytes(byte b[], int off, int len, boolean append)
+ throws IOException;
/**
* Writes b.length bytes from the specified byte array
@@ -276,7 +301,7 @@ class FileOutputStream extends OutputStream
* @exception IOException if an I/O error occurs.
*/
public void write(byte b[]) throws IOException {
- writeBytes(b, 0, b.length);
+ writeBytes(b, 0, b.length, append);
}
/**
@@ -289,7 +314,7 @@ class FileOutputStream extends OutputStream
* @exception IOException if an I/O error occurs.
*/
public void write(byte b[], int off, int len) throws IOException {
- writeBytes(b, off, len);
+ writeBytes(b, off, len, append);
}
/**
@@ -372,7 +397,7 @@ class FileOutputStream extends OutputStream
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
- channel = FileChannelImpl.open(fd, false, true, this);
+ channel = FileChannelImpl.open(fd, false, true, append, this);
/*
* Increment fd's use count. Invoking the channel's close()
diff --git a/jdk/src/share/classes/java/lang/ProcessBuilder.java b/jdk/src/share/classes/java/lang/ProcessBuilder.java
index 97ce45cdf65..ebc372380bb 100644
--- a/jdk/src/share/classes/java/lang/ProcessBuilder.java
+++ b/jdk/src/share/classes/java/lang/ProcessBuilder.java
@@ -537,7 +537,11 @@ public final class ProcessBuilder
*/
public File file() { return null; }
- FileOutputStream toFileOutputStream() throws IOException {
+ /**
+ * When redirected to a destination file, indicates if the output
+ * is to be written to the end of the file.
+ */
+ boolean append() {
throw new UnsupportedOperationException();
}
@@ -588,9 +592,7 @@ public final class ProcessBuilder
public String toString() {
return "redirect to write to file \"" + file + "\"";
}
- FileOutputStream toFileOutputStream() throws IOException {
- return new FileOutputStream(file, false);
- }
+ boolean append() { return false; }
};
}
@@ -620,9 +622,7 @@ public final class ProcessBuilder
public String toString() {
return "redirect to append to file \"" + file + "\"";
}
- FileOutputStream toFileOutputStream() throws IOException {
- return new FileOutputStream(file, true);
- }
+ boolean append() { return true; }
};
}
diff --git a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java
index e37fe799d84..32d0be649d3 100644
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -39,13 +39,12 @@ import sun.security.action.GetPropertyAction;
public class FileChannelImpl
extends FileChannel
{
-
- // Used to make native read and write calls
- private static final FileDispatcher nd;
-
// Memory allocation size for mapping buffers
private static final long allocationGranularity;
+ // Used to make native read and write calls
+ private final FileDispatcher nd;
+
// File descriptor
private final FileDescriptor fd;
@@ -63,22 +62,29 @@ public class FileChannelImpl
private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, boolean readable,
- boolean writable, Object parent)
+ boolean writable, boolean append, Object parent)
{
this.fd = fd;
this.readable = readable;
this.writable = writable;
this.parent = parent;
+ this.nd = new FileDispatcherImpl(append);
}
- // Invoked by getChannel() methods
- // of java.io.File{Input,Output}Stream and RandomAccessFile
- //
+ // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
public static FileChannel open(FileDescriptor fd,
boolean readable, boolean writable,
Object parent)
{
- return new FileChannelImpl(fd, readable, writable, parent);
+ return new FileChannelImpl(fd, readable, writable, false, parent);
+ }
+
+ // Used by FileOutputStream.getChannel
+ public static FileChannel open(FileDescriptor fd,
+ boolean readable, boolean writable,
+ boolean append, Object parent)
+ {
+ return new FileChannelImpl(fd, readable, writable, append, parent);
}
private void ensureOpen() throws IOException {
@@ -704,6 +710,9 @@ public class FileChannelImpl
private static class Unmapper
implements Runnable
{
+ // may be required to close file
+ private static final NativeDispatcher nd = new FileDispatcherImpl();
+
// keep track of mapped buffer usage
static volatile int count;
static volatile long totalSize;
@@ -1119,7 +1128,6 @@ public class FileChannelImpl
static {
Util.load();
allocationGranularity = initIDs();
- nd = new FileDispatcherImpl();
}
}
diff --git a/jdk/src/share/native/java/io/RandomAccessFile.c b/jdk/src/share/native/java/io/RandomAccessFile.c
index a8c3390b677..437bab6b831 100644
--- a/jdk/src/share/native/java/io/RandomAccessFile.c
+++ b/jdk/src/share/native/java/io/RandomAccessFile.c
@@ -76,13 +76,13 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv *env,
JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_write(JNIEnv *env, jobject this, jint byte) {
- writeSingle(env, this, byte, raf_fd);
+ writeSingle(env, this, byte, JNI_FALSE, raf_fd);
}
JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) {
- writeBytes(env, this, bytes, off, len, raf_fd);
+ writeBytes(env, this, bytes, off, len, JNI_FALSE, raf_fd);
}
JNIEXPORT jlong JNICALL
diff --git a/jdk/src/share/native/java/io/io_util.c b/jdk/src/share/native/java/io/io_util.c
index 986416e20d8..cef7d272ba2 100644
--- a/jdk/src/share/native/java/io/io_util.c
+++ b/jdk/src/share/native/java/io/io_util.c
@@ -127,7 +127,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
}
void
-writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
+writeSingle(JNIEnv *env, jobject this, jint byte, jboolean append, jfieldID fid) {
// Discard the 24 high-order bits of byte. See OutputStream#write(int)
char c = (char) byte;
jint n;
@@ -136,7 +136,11 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
JNU_ThrowIOException(env, "Stream Closed");
return;
}
- n = IO_Write(fd, &c, 1);
+ if (append == JNI_TRUE) {
+ n = IO_Append(fd, &c, 1);
+ } else {
+ n = IO_Write(fd, &c, 1);
+ }
if (n == JVM_IO_ERR) {
JNU_ThrowIOExceptionWithLastError(env, "Write error");
} else if (n == JVM_IO_INTR) {
@@ -146,7 +150,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
void
writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
- jint off, jint len, jfieldID fid)
+ jint off, jint len, jboolean append, jfieldID fid)
{
jint n;
char stackBuf[BUF_SIZE];
@@ -185,7 +189,11 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
JNU_ThrowIOException(env, "Stream Closed");
break;
}
- n = IO_Write(fd, buf+off, len);
+ if (append == JNI_TRUE) {
+ n = IO_Append(fd, buf+off, len);
+ } else {
+ n = IO_Write(fd, buf+off, len);
+ }
if (n == JVM_IO_ERR) {
JNU_ThrowIOExceptionWithLastError(env, "Write error");
break;
diff --git a/jdk/src/share/native/java/io/io_util.h b/jdk/src/share/native/java/io/io_util.h
index 436acdff16d..b98c5274a48 100644
--- a/jdk/src/share/native/java/io/io_util.h
+++ b/jdk/src/share/native/java/io/io_util.h
@@ -41,9 +41,9 @@ extern jfieldID IO_handle_fdID;
jint readSingle(JNIEnv *env, jobject this, jfieldID fid);
jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
jint len, jfieldID fid);
-void writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid);
+void writeSingle(JNIEnv *env, jobject this, jint byte, jboolean append, jfieldID fid);
void writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
- jint len, jfieldID fid);
+ jint len, jboolean append, jfieldID fid);
void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
void throwFileNotFoundException(JNIEnv *env, jstring path);
diff --git a/jdk/src/solaris/classes/java/lang/ProcessImpl.java b/jdk/src/solaris/classes/java/lang/ProcessImpl.java
index 5c291b138c3..ed0c6397677 100644
--- a/jdk/src/solaris/classes/java/lang/ProcessImpl.java
+++ b/jdk/src/solaris/classes/java/lang/ProcessImpl.java
@@ -111,7 +111,8 @@ final class ProcessImpl {
else if (redirects[1] == Redirect.INHERIT)
std_fds[1] = 1;
else {
- f1 = redirects[1].toFileOutputStream();
+ f1 = new FileOutputStream(redirects[1].file(),
+ redirects[1].append());
std_fds[1] = fdAccess.get(f1.getFD());
}
@@ -120,7 +121,8 @@ final class ProcessImpl {
else if (redirects[2] == Redirect.INHERIT)
std_fds[2] = 2;
else {
- f2 = redirects[2].toFileOutputStream();
+ f2 = new FileOutputStream(redirects[2].file(),
+ redirects[2].append());
std_fds[2] = fdAccess.get(f2.getFD());
}
}
diff --git a/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java b/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java
index 6f17340372e..f56d317cc91 100644
--- a/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java
+++ b/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java
@@ -35,6 +35,13 @@ class FileDispatcherImpl extends FileDispatcher
init();
}
+ FileDispatcherImpl(boolean append) {
+ /* append is ignored */
+ }
+
+ FileDispatcherImpl() {
+ }
+
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
}
diff --git a/jdk/src/solaris/native/java/io/FileOutputStream_md.c b/jdk/src/solaris/native/java/io/FileOutputStream_md.c
index 1d71052ec56..ffc2011797d 100644
--- a/jdk/src/solaris/native/java/io/FileOutputStream_md.c
+++ b/jdk/src/solaris/native/java/io/FileOutputStream_md.c
@@ -60,14 +60,14 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
}
JNIEXPORT void JNICALL
-Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
- writeSingle(env, this, byte, fos_fd);
+Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
+ writeSingle(env, this, byte, append, fos_fd);
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
- jobject this, jbyteArray bytes, jint off, jint len) {
- writeBytes(env, this, bytes, off, len, fos_fd);
+ jobject this, jbyteArray bytes, jint off, jint len, jboolean append) {
+ writeBytes(env, this, bytes, off, len, append, fos_fd);
}
JNIEXPORT void JNICALL
diff --git a/jdk/src/solaris/native/java/io/io_util_md.h b/jdk/src/solaris/native/java/io/io_util_md.h
index 378fbcb1ebc..b5fe90e6a73 100644
--- a/jdk/src/solaris/native/java/io/io_util_md.h
+++ b/jdk/src/solaris/native/java/io/io_util_md.h
@@ -53,8 +53,9 @@
#define THIS_FD(obj) (*env)->GetIntField(env, obj, IO_fd_fdID)
/*
- * Route the routines through HPI
+ * Route the routines through VM
*/
+#define IO_Append JVM_Write
#define IO_Write JVM_Write
#define IO_Sync JVM_Sync
#define IO_Read JVM_Read
diff --git a/jdk/src/windows/classes/java/lang/ProcessImpl.java b/jdk/src/windows/classes/java/lang/ProcessImpl.java
index c0a0daa09a8..b3357dd0c9d 100644
--- a/jdk/src/windows/classes/java/lang/ProcessImpl.java
+++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java
@@ -35,6 +35,8 @@ import java.io.FileDescriptor;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.lang.ProcessBuilder.Redirect;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
/* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes.
@@ -47,6 +49,35 @@ final class ProcessImpl extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
+ /**
+ * Open a file for writing. If {@code append} is {@code true} then the file
+ * is opened for atomic append directly and a FileOutputStream constructed
+ * with the resulting handle. This is because a FileOutputStream created
+ * to append to a file does not open the file in a manner that guarantees
+ * that writes by the child process will be atomic.
+ */
+ private static FileOutputStream newFileOutputStream(File f, boolean append)
+ throws IOException
+ {
+ if (append) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkWrite(f.getPath());
+ long handle = openForAtomicAppend(f.getPath());
+ final FileDescriptor fd = new FileDescriptor();
+ fdAccess.setHandle(fd, handle);
+ return AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public FileOutputStream run() {
+ return new FileOutputStream(fd);
+ }
+ }
+ );
+ } else {
+ return new FileOutputStream(f);
+ }
+ }
+
// System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[],
java.util.Map environment,
@@ -82,7 +113,8 @@ final class ProcessImpl extends Process {
else if (redirects[1] == Redirect.INHERIT)
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
else {
- f1 = redirects[1].toFileOutputStream();
+ f1 = newFileOutputStream(redirects[1].file(),
+ redirects[1].append());
stdHandles[1] = fdAccess.getHandle(f1.getFD());
}
@@ -91,7 +123,8 @@ final class ProcessImpl extends Process {
else if (redirects[2] == Redirect.INHERIT)
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
else {
- f2 = redirects[2].toFileOutputStream();
+ f2 = newFileOutputStream(redirects[2].file(),
+ redirects[2].append());
stdHandles[2] = fdAccess.getHandle(f2.getFD());
}
}
@@ -251,5 +284,15 @@ final class ProcessImpl extends Process {
boolean redirectErrorStream)
throws IOException;
+ /**
+ * Opens a file for atomic append. The file is created if it doesn't
+ * already exist.
+ *
+ * @param file the file to open or create
+ * @return the native HANDLE
+ */
+ private static native long openForAtomicAppend(String path)
+ throws IOException;
+
private static native boolean closeHandle(long handle);
}
diff --git a/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java b/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java
index 2cdc8f8fe9f..43f0745be40 100644
--- a/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java
+++ b/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java
@@ -35,6 +35,20 @@ class FileDispatcherImpl extends FileDispatcher
Util.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);
+ }
+
int read(FileDescriptor fd, long address, int len)
throws IOException
{
@@ -54,7 +68,7 @@ class FileDispatcherImpl extends FileDispatcher
}
int write(FileDescriptor fd, long address, int len) throws IOException {
- return write0(fd, address, len);
+ return write0(fd, address, len, append);
}
int pwrite(FileDescriptor fd, long address, int len,
@@ -66,7 +80,7 @@ class FileDispatcherImpl extends FileDispatcher
}
long writev(FileDescriptor fd, long address, int len) throws IOException {
- return writev0(fd, address, len);
+ return writev0(fd, address, len, append);
}
int force(FileDescriptor fd, boolean metaData) throws IOException {
@@ -116,13 +130,13 @@ class FileDispatcherImpl extends FileDispatcher
static native long readv0(FileDescriptor fd, long address, int len)
throws IOException;
- static native int write0(FileDescriptor fd, long address, int len)
+ static native int write0(FileDescriptor fd, long address, int len, boolean append)
throws IOException;
static native int pwrite0(FileDescriptor fd, long address, int len,
long position) throws IOException;
- static native long writev0(FileDescriptor fd, long address, int len)
+ static native long writev0(FileDescriptor fd, long address, int len, boolean append)
throws IOException;
static native int force0(FileDescriptor fd, boolean metaData)
diff --git a/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java b/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java
index 3795a6f40d2..736ea7b1ea0 100644
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java
@@ -157,7 +157,7 @@ class WindowsChannelFactory {
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
- return FileChannelImpl.open(fdObj, flags.read, flags.write, null);
+ return FileChannelImpl.open(fdObj, flags.read, flags.write, flags.append, null);
}
/**
@@ -230,7 +230,7 @@ class WindowsChannelFactory {
if (flags.read)
dwDesiredAccess |= GENERIC_READ;
if (flags.write)
- dwDesiredAccess |= (flags.append) ? FILE_APPEND_DATA : GENERIC_WRITE;
+ dwDesiredAccess |= GENERIC_WRITE;
int dwShareMode = 0;
if (flags.shareRead)
diff --git a/jdk/src/windows/native/java/io/FileOutputStream_md.c b/jdk/src/windows/native/java/io/FileOutputStream_md.c
index 221ba504f5f..23c754d4667 100644
--- a/jdk/src/windows/native/java/io/FileOutputStream_md.c
+++ b/jdk/src/windows/native/java/io/FileOutputStream_md.c
@@ -61,14 +61,15 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
}
JNIEXPORT void JNICALL
-Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
- writeSingle(env, this, byte, fos_fd);
+Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
+ writeSingle(env, this, byte, append, fos_fd);
}
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
- jobject this, jbyteArray bytes, jint off, jint len) {
- writeBytes(env, this, bytes, off, len, fos_fd);
+ jobject this, jbyteArray bytes, jint off, jint len, jboolean append)
+{
+ writeBytes(env, this, bytes, off, len, append, fos_fd);
}
JNIEXPORT void JNICALL
diff --git a/jdk/src/windows/native/java/io/io_util_md.c b/jdk/src/windows/native/java/io/io_util_md.c
index 722913f775a..a81e39e9851 100644
--- a/jdk/src/windows/native/java/io/io_util_md.c
+++ b/jdk/src/windows/native/java/io/io_util_md.c
@@ -225,14 +225,7 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
jlong
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
{
- /* To implement O_APPEND, we use the strategy from
- http://msdn2.microsoft.com/en-us/library/aa363858.aspx
- "You can get atomic append by opening a file with
- FILE_APPEND_DATA access and _without_ FILE_WRITE_DATA access.
- If you do this then all writes will ignore the current file
- pointer and be done at the end-of file." */
const DWORD access =
- (flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
(flags & O_WRONLY) ? GENERIC_WRITE :
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
GENERIC_READ;
@@ -511,24 +504,42 @@ handleRead(jlong fd, void *buf, jint len)
return read;
}
-JNIEXPORT
-size_t
-handleWrite(jlong fd, const void *buf, jint len)
+static size_t writeInternal(jlong fd, const void *buf, jint len, jboolean append)
{
BOOL result = 0;
DWORD written = 0;
HANDLE h = (HANDLE)fd;
if (h != INVALID_HANDLE_VALUE) {
- result = WriteFile(h, /* File handle to write */
- buf, /* pointers to the buffers */
- len, /* number of bytes to write */
- &written, /* receives number of bytes written */
- NULL); /* no overlapped struct */
+ OVERLAPPED ov;
+ LPOVERLAPPED lpOv;
+ if (append == JNI_TRUE) {
+ ov.Offset = (DWORD)0xFFFFFFFF;
+ ov.OffsetHigh = (DWORD)0xFFFFFFFF;
+ ov.hEvent = NULL;
+ lpOv = &ov;
+ } else {
+ lpOv = NULL;
+ }
+ result = WriteFile(h, /* File handle to write */
+ buf, /* pointers to the buffers */
+ len, /* number of bytes to write */
+ &written, /* receives number of bytes written */
+ lpOv); /* overlapped struct */
}
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
return -1;
}
- return written;
+ return (size_t)written;
+}
+
+JNIEXPORT
+size_t handleWrite(jlong fd, const void *buf, jint len) {
+ return writeInternal(fd, buf, len, JNI_FALSE);
+}
+
+JNIEXPORT
+size_t handleAppend(jlong fd, const void *buf, jint len) {
+ return writeInternal(fd, buf, len, JNI_TRUE);
}
jint
diff --git a/jdk/src/windows/native/java/io/io_util_md.h b/jdk/src/windows/native/java/io/io_util_md.h
index 6b6b89b6397..97a68c2feba 100644
--- a/jdk/src/windows/native/java/io/io_util_md.h
+++ b/jdk/src/windows/native/java/io/io_util_md.h
@@ -41,6 +41,7 @@ JNIEXPORT int handleSync(jlong fd);
int handleSetLength(jlong fd, jlong length);
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
+JNIEXPORT size_t handleAppend(jlong fd, const void *buf, jint len);
jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
jlong handleLseek(jlong fd, jlong offset, jint whence);
@@ -74,8 +75,9 @@ jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags);
#define THIS_FD(obj) (*env)->GetLongField(env, obj, IO_handle_fdID)
/*
- * Route the routines away from HPI layer
+ * Route the routines away from VM
*/
+#define IO_Append handleAppend
#define IO_Write handleWrite
#define IO_Sync handleSync
#define IO_Read handleRead
diff --git a/jdk/src/windows/native/java/lang/ProcessImpl_md.c b/jdk/src/windows/native/java/lang/ProcessImpl_md.c
index e5fe23e71ec..afc5e7d2529 100644
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c
@@ -315,3 +315,51 @@ Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle
{
return CloseHandle((HANDLE) handle);
}
+
+/**
+ * Returns a copy of the Unicode characters of a string. Fow now this
+ * function doesn't handle long path names and other issues.
+ */
+static WCHAR* getPath(JNIEnv *env, jstring ps) {
+ WCHAR *pathbuf = NULL;
+ const jchar *chars = (*(env))->GetStringChars(env, ps, NULL);
+ if (chars != NULL) {
+ size_t pathlen = wcslen(chars);
+ pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
+ if (pathbuf == NULL) {
+ JNU_ThrowOutOfMemoryError(env, NULL);
+ } else {
+ wcscpy(pathbuf, chars);
+ }
+ (*env)->ReleaseStringChars(env, ps, chars);
+ }
+ return pathbuf;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_lang_ProcessImpl_openForAtomicAppend(JNIEnv *env, jclass ignored, jstring path)
+{
+ const DWORD access = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA);
+ const DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ const DWORD disposition = OPEN_ALWAYS;
+ const DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ HANDLE h;
+ WCHAR *pathbuf = getPath(env, path);
+ if (pathbuf == NULL) {
+ /* Exception already pending */
+ return -1;
+ }
+ h = CreateFileW(
+ pathbuf, /* Wide char path name */
+ access, /* Read and/or write permission */
+ sharing, /* File sharing flags */
+ NULL, /* Security attributes */
+ disposition, /* creation disposition */
+ flagsAndAttributes, /* flags and attributes */
+ NULL);
+ free(pathbuf);
+ if (h == INVALID_HANDLE_VALUE) {
+ JNU_ThrowIOExceptionWithLastError(env, "CreateFileW");
+ }
+ return ptr_to_jlong(h);
+}
diff --git a/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c b/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c
index f4d0b17d63b..2a146c284fa 100644
--- a/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c
+++ b/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c
@@ -184,18 +184,28 @@ Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo,
- jlong address, jint len)
+ jlong address, jint len, jboolean append)
{
BOOL result = 0;
DWORD written = 0;
HANDLE h = (HANDLE)(handleval(env, fdo));
if (h != INVALID_HANDLE_VALUE) {
+ OVERLAPPED ov;
+ LPOVERLAPPED lpOv;
+ if (append == JNI_TRUE) {
+ ov.Offset = (DWORD)0xFFFFFFFF;
+ ov.OffsetHigh = (DWORD)0xFFFFFFFF;
+ ov.hEvent = NULL;
+ lpOv = &ov;
+ } else {
+ lpOv = NULL;
+ }
result = WriteFile(h, /* File handle to write */
(LPCVOID)address, /* pointers to the buffers */
len, /* number of bytes to write */
&written, /* receives number of bytes written */
- NULL); /* no overlapped struct */
+ lpOv); /* overlapped struct */
}
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
@@ -207,7 +217,7 @@ Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo,
- jlong address, jint len)
+ jlong address, jint len, jboolean append)
{
BOOL result = 0;
DWORD written = 0;
@@ -219,7 +229,16 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd
int i = 0;
DWORD num = 0;
struct iovec *iovecp = (struct iovec *)jlong_to_ptr(address);
-
+ OVERLAPPED ov;
+ LPOVERLAPPED lpOv;
+ if (append == JNI_TRUE) {
+ ov.Offset = (DWORD)0xFFFFFFFF;
+ ov.OffsetHigh = (DWORD)0xFFFFFFFF;
+ ov.hEvent = NULL;
+ lpOv = &ov;
+ } else {
+ lpOv = NULL;
+ }
for(i=0; i 0) {
totalWritten += written;
}
@@ -444,9 +463,10 @@ Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz,
}
JNIEXPORT jlong JNICALL
-Java_sun_nio_ch_FileDispatcherImpl_duplicateHandle(JNIEnv *env, jclass this, jlong hFile)
+Java_sun_nio_ch_FileDispatcherImpl_duplicateHandle(JNIEnv *env, jclass this, jlong handle)
{
HANDLE hProcess = GetCurrentProcess();
+ HANDLE hFile = jlong_to_ptr(handle);
HANDLE hResult;
BOOL res = DuplicateHandle(hProcess, hFile, hProcess, &hResult, 0, FALSE,
DUPLICATE_SAME_ACCESS);
diff --git a/jdk/test/java/nio/channels/FileChannel/AtomicAppend.java b/jdk/test/java/nio/channels/FileChannel/AtomicAppend.java
new file mode 100644
index 00000000000..1420318d5d7
--- /dev/null
+++ b/jdk/test/java/nio/channels/FileChannel/AtomicAppend.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010, 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
+ * @summary Check that appends are atomic
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import static java.nio.file.StandardOpenOption.*;
+
+public class AtomicAppend {
+ static final Random rand = new Random();
+
+ // Open file for appending, returning FileChannel
+ static FileChannel newFileChannel(File file) throws IOException {
+ if (rand.nextBoolean()) {
+ return new FileOutputStream(file, true).getChannel();
+ } else {
+ return FileChannel.open(file.toPath(), APPEND);
+ }
+ }
+
+ // Open file for append, returning OutputStream
+ static OutputStream newOutputStream(File file) throws IOException {
+ if (rand.nextBoolean()) {
+ return new FileOutputStream(file, true);
+ } else {
+ return file.toPath().newOutputStream(APPEND);
+ }
+ }
+
+ // write a byte to the given channel
+ static void write(FileChannel fc, int b) throws IOException {
+ ByteBuffer buf = ByteBuffer.allocate(1);
+ buf.put((byte)b);
+ buf.flip();
+ if (rand.nextBoolean()) {
+ ByteBuffer[] bufs = new ByteBuffer[1];
+ bufs[0] = buf;
+ fc.write(bufs);
+ } else {
+ fc.write(buf);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ final int nThreads = 16;
+ final int writes = 1000;
+ final File file = File.createTempFile("foo", null);
+ try {
+ ExecutorService pool = Executors.newFixedThreadPool(nThreads);
+ for (int i = 0; i < nThreads; i++)
+ pool.execute(new Runnable() { public void run() {
+ try {
+ // randomly choose FileChannel or OutputStream
+ if (rand.nextBoolean()) {
+ try (FileChannel fc = newFileChannel(file)) {
+ for (int j=0; j newSize) {
- if (c.position() != newSize)
- throw new RuntimeException("Position greater than size");
- } else {
- if (c.position() != position)
- throw new RuntimeException("Truncate changed position");
+ if (position > newSize) {
+ if (fc.position() != newSize)
+ throw new RuntimeException("Position greater than size");
+ } else {
+ if (fc.position() != position)
+ throw new RuntimeException("Truncate changed position");
+ };
+ }
+ }
+ }
+
+ /**
+ * Test behavior of truncate method when file is opened for append
+ */
+ static void appendTest(File blah) throws Exception {
+ for (int i=0; i<10; i++) {
+ long testSize = generator.nextInt(1000) + 10;
+ initTestFile(blah, testSize);
+ FileChannel fc = (i < 5) ?
+ new FileOutputStream(blah, true).getChannel() :
+ FileChannel.open(blah.toPath(), APPEND);
+ try (fc) {
+ // truncate file
+ long newSize = generator.nextInt((int)testSize);
+ fc.truncate(newSize);
+ if (fc.size() != newSize)
+ throw new RuntimeException("Truncate failed");
+
+ // write one byte
+ ByteBuffer buf = ByteBuffer.allocate(1);
+ buf.put((byte)'x');
+ buf.flip();
+ fc.write(buf);
+ if (fc.size() != (newSize+1))
+ throw new RuntimeException("Unexpected size");
}
-
- c.close();
- fis.close();
}
- blah.delete();
}
/**