OpenJDK / loom / loom
changeset 7515:43202796198e
6709457: (fc) lock/tryLock() throws IOException "Access is denied" when file opened for append [win]
Reviewed-by: chegar
line wrap: on
line diff
--- 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); - c.position(position); + long position = generator.nextInt((int)testSize); + fc.position(position); - long newSize = generator.nextInt((int)testSize); - c.truncate(newSize); + long newSize = generator.nextInt((int)testSize); + fc.truncate(newSize); - if (c.size() != newSize) - throw new RuntimeException("Truncate failed"); + 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"); + 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"); + }; } + } + } - c.close(); - fis.close(); + /** + * 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"); + } } - blah.delete(); } /**