6709457: (fc) lock/tryLock() throws IOException "Access is denied" when file opened for append [win]
Reviewed-by: chegar
--- a/jdk/src/share/classes/java/io/FileOutputStream.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/classes/java/io/FileOutputStream.java Wed Dec 01 13:49:02 2010 +0000
@@ -56,7 +56,15 @@
*/
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 @@
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 @@
if (security != null) {
security.checkWrite(fdObj);
}
- fd = fdObj;
+ this.fd = fdObj;
+ this.append = false;
/*
* FileDescriptor is being shared by streams.
@@ -251,22 +262,36 @@
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 <code>write</code> method of <code>OutputStream</code>.
*
* @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 <code>b.length</code> bytes from the specified byte array
@@ -276,7 +301,7 @@
* @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 @@
* @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 @@
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()
--- a/jdk/src/share/classes/java/lang/ProcessBuilder.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/classes/java/lang/ProcessBuilder.java Wed Dec 01 13:49:02 2010 +0000
@@ -537,7 +537,11 @@
*/
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 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 String toString() {
return "redirect to append to file \"" + file + "\"";
}
- FileOutputStream toFileOutputStream() throws IOException {
- return new FileOutputStream(file, true);
- }
+ boolean append() { return true; }
};
}
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java Wed Dec 01 13:49:02 2010 +0000
@@ -39,12 +39,11 @@
public class FileChannelImpl
extends FileChannel
{
+ // Memory allocation size for mapping buffers
+ private static final long allocationGranularity;
// Used to make native read and write calls
- private static final FileDispatcher nd;
-
- // Memory allocation size for mapping buffers
- private static final long allocationGranularity;
+ private final FileDispatcher nd;
// File descriptor
private final FileDescriptor fd;
@@ -63,22 +62,29 @@
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 @@
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 @@
static {
Util.load();
allocationGranularity = initIDs();
- nd = new FileDispatcherImpl();
}
}
--- a/jdk/src/share/native/java/io/RandomAccessFile.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/native/java/io/RandomAccessFile.c Wed Dec 01 13:49:02 2010 +0000
@@ -76,13 +76,13 @@
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
--- a/jdk/src/share/native/java/io/io_util.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/native/java/io/io_util.c Wed Dec 01 13:49:02 2010 +0000
@@ -127,7 +127,7 @@
}
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 @@
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 @@
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 @@
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;
--- a/jdk/src/share/native/java/io/io_util.h Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/share/native/java/io/io_util.h Wed Dec 01 13:49:02 2010 +0000
@@ -41,9 +41,9 @@
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);
--- a/jdk/src/solaris/classes/java/lang/ProcessImpl.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/solaris/classes/java/lang/ProcessImpl.java Wed Dec 01 13:49:02 2010 +0000
@@ -111,7 +111,8 @@
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 @@
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());
}
}
--- a/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java Wed Dec 01 13:49:02 2010 +0000
@@ -35,6 +35,13 @@
init();
}
+ FileDispatcherImpl(boolean append) {
+ /* append is ignored */
+ }
+
+ FileDispatcherImpl() {
+ }
+
int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len);
}
--- a/jdk/src/solaris/native/java/io/FileOutputStream_md.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/solaris/native/java/io/FileOutputStream_md.c Wed Dec 01 13:49:02 2010 +0000
@@ -60,14 +60,14 @@
}
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
--- a/jdk/src/solaris/native/java/io/io_util_md.h Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/solaris/native/java/io/io_util_md.h Wed Dec 01 13:49:02 2010 +0000
@@ -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
--- a/jdk/src/windows/classes/java/lang/ProcessImpl.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java Wed Dec 01 13:49:02 2010 +0000
@@ -35,6 +35,8 @@
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 @@
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<FileOutputStream>() {
+ 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<String,String> environment,
@@ -82,7 +113,8 @@
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 @@
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 @@
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);
}
--- a/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java Wed Dec 01 13:49:02 2010 +0000
@@ -35,6 +35,20 @@
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 @@
}
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 @@
}
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 @@
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)
--- a/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java Wed Dec 01 13:49:02 2010 +0000
@@ -157,7 +157,7 @@
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 @@
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)
--- a/jdk/src/windows/native/java/io/FileOutputStream_md.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/native/java/io/FileOutputStream_md.c Wed Dec 01 13:49:02 2010 +0000
@@ -61,14 +61,15 @@
}
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
--- a/jdk/src/windows/native/java/io/io_util_md.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/native/java/io/io_util_md.c Wed Dec 01 13:49:02 2010 +0000
@@ -225,14 +225,7 @@
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 @@
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
--- a/jdk/src/windows/native/java/io/io_util_md.h Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/native/java/io/io_util_md.h Wed Dec 01 13:49:02 2010 +0000
@@ -41,6 +41,7 @@
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 @@
#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
--- a/jdk/src/windows/native/java/lang/ProcessImpl_md.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/native/java/lang/ProcessImpl_md.c Wed Dec 01 13:49:02 2010 +0000
@@ -315,3 +315,51 @@
{
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);
+}
--- a/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/src/windows/native/sun/nio/ch/FileDispatcherImpl.c Wed Dec 01 13:49:02 2010 +0000
@@ -184,18 +184,28 @@
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 @@
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 @@
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<len; i++) {
loc = (LPVOID)jlong_to_ptr(iovecp[i].iov_base);
num = iovecp[i].iov_len;
@@ -227,7 +246,7 @@
loc, /* pointers to the buffers */
num, /* number of bytes to write */
&written,/* receives number of bytes written */
- NULL); /* no overlapped struct */
+ lpOv); /* overlapped struct */
if (written > 0) {
totalWritten += written;
}
@@ -444,9 +463,10 @@
}
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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/channels/FileChannel/AtomicAppend.java Wed Dec 01 13:49:02 2010 +0000
@@ -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<writes; j++) write(fc, 'x');
+ }
+ } else {
+ try (OutputStream out = newOutputStream(file)) {
+ for (int j = 0; j<writes; j++) out.write('x');
+ }
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }});
+ pool.shutdown();
+ pool.awaitTermination(1L, TimeUnit.MINUTES);
+ if (file.length() != (long) (nThreads * writes))
+ throw new RuntimeException("File not expected length");
+ } finally {
+ file.delete();
+ }
+ }
+}
--- a/jdk/test/java/nio/channels/FileChannel/Lock.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/test/java/nio/channels/FileChannel/Lock.java Wed Dec 01 13:49:02 2010 +0000
@@ -22,13 +22,13 @@
*/
/* @test
- * @bug 4429043 4493595 6332756
+ * @bug 4429043 4493595 6332756 6709457
* @summary The FileChannel file locking
*/
import java.io.*;
import java.nio.channels.*;
-import java.nio.*;
+import static java.nio.file.StandardOpenOption.*;
/**
* Testing FileChannel's lock method.
@@ -55,6 +55,7 @@
test2(blah, true);
test2(blah, false);
test3(blah);
+ test4(blah);
blah.delete();
}
@@ -163,6 +164,24 @@
fc1.close();
fc2.close();
}
+
+ /**
+ * Test file locking when file is opened for append
+ */
+ static void test4(File blah) throws Exception {
+ try (FileChannel fc = new FileOutputStream(blah, true).getChannel()) {
+ fc.tryLock().release();
+ fc.tryLock(0L, 1L, false).release();
+ fc.lock().release();
+ fc.lock(0L, 1L, false).release();
+ }
+ try (FileChannel fc = FileChannel.open(blah.toPath(), APPEND)) {
+ fc.tryLock().release();
+ fc.tryLock(0L, 1L, false).release();
+ fc.lock().release();
+ fc.lock(0L, 1L, false).release();
+ }
+ }
}
class MadWriter {
--- a/jdk/test/java/nio/channels/FileChannel/Truncate.java Wed Nov 24 09:51:31 2010 +0000
+++ b/jdk/test/java/nio/channels/FileChannel/Truncate.java Wed Dec 01 13:49:02 2010 +0000
@@ -22,14 +22,14 @@
*/
/* @test
- * @bug 6191269
+ * @bug 6191269 6709457
* @summary Test truncate method of FileChannel
*/
import java.io.*;
-import java.nio.MappedByteBuffer;
-import java.nio.channels.*;
+import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import static java.nio.file.StandardOpenOption.*;
import java.util.Random;
@@ -38,43 +38,79 @@
*/
public class Truncate {
-
- private static Random generator = new Random();
-
- private static File blah;
+ private static final Random generator = new Random();
public static void main(String[] args) throws Exception {
- blah = File.createTempFile("blah", null);
+ File blah = File.createTempFile("blah", null);
blah.deleteOnExit();
+ try {
+ basicTest(blah);
+ appendTest(blah);
+ } finally {
+ blah.delete();
+ }
+ }
+
+ /**
+ * Basic test of asserts in truncate's specification.
+ */
+ static void basicTest(File blah) throws Exception {
for(int i=0; i<100; i++) {
long testSize = generator.nextInt(1000) + 10;
initTestFile(blah, testSize);
- RandomAccessFile fis = new RandomAccessFile(blah, "rw");
- FileChannel c = fis.getChannel();
- if (c.size() != testSize)
- throw new RuntimeException("Size failed");
+ FileChannel fc = (i < 50) ?
+ new RandomAccessFile(blah, "rw").getChannel() :
+ FileChannel.open(blah.toPath(), READ, WRITE);
+ try (fc) {
+ if (fc.size() != testSize)
+ throw new RuntimeException("Size failed");
+
+ long position = generator.nextInt((int)testSize);
+ fc.position(position);
+
+ long newSize = generator.nextInt((int)testSize);
+ fc.truncate(newSize);
- long position = generator.nextInt((int)testSize);
- c.position(position);
+ if (fc.size() != newSize)
+ throw new RuntimeException("Truncate failed");
- long newSize = generator.nextInt((int)testSize);
- c.truncate(newSize);
+ 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");
+ };
+ }
+ }
+ }
- if (c.size() != newSize)
- throw new RuntimeException("Truncate failed");
+ /**
+ * 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");
- if (position > newSize) {
- if (c.position() != newSize)
- throw new RuntimeException("Position greater than size");
- } else {
- if (c.position() != position)
- throw new RuntimeException("Truncate changed position");
+ // 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();
}
/**