提交 c10132a6 编写于 作者: A alanb

6709457: (fc) lock/tryLock() throws IOException "Access is denied" when file...

6709457: (fc) lock/tryLock() throws IOException "Access is denied" when file opened for append [win]
Reviewed-by: chegar
上级 eb8c7086
...@@ -56,7 +56,15 @@ class FileOutputStream extends OutputStream ...@@ -56,7 +56,15 @@ class FileOutputStream extends OutputStream
*/ */
private final FileDescriptor fd; 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 final Object closeLock = new Object();
private volatile boolean closed = false; private volatile boolean closed = false;
...@@ -196,7 +204,9 @@ class FileOutputStream extends OutputStream ...@@ -196,7 +204,9 @@ class FileOutputStream extends OutputStream
if (name == null) { if (name == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
fd = new FileDescriptor(); this.fd = new FileDescriptor();
this.append = append;
fd.incrementAndGetUseCount(); fd.incrementAndGetUseCount();
open(name, append); open(name, append);
} }
...@@ -232,7 +242,8 @@ class FileOutputStream extends OutputStream ...@@ -232,7 +242,8 @@ class FileOutputStream extends OutputStream
if (security != null) { if (security != null) {
security.checkWrite(fdObj); security.checkWrite(fdObj);
} }
fd = fdObj; this.fd = fdObj;
this.append = false;
/* /*
* FileDescriptor is being shared by streams. * FileDescriptor is being shared by streams.
...@@ -250,6 +261,15 @@ class FileOutputStream extends OutputStream ...@@ -250,6 +261,15 @@ class FileOutputStream extends OutputStream
private native void open(String name, boolean append) private native void open(String name, boolean append)
throws FileNotFoundException; 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 * Writes the specified byte to this file output stream. Implements
* the <code>write</code> method of <code>OutputStream</code>. * the <code>write</code> method of <code>OutputStream</code>.
...@@ -257,16 +277,21 @@ class FileOutputStream extends OutputStream ...@@ -257,16 +277,21 @@ class FileOutputStream extends OutputStream
* @param b the byte to be written. * @param b the byte to be written.
* @exception IOException if an I/O error occurs. * @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. * Writes a sub array as a sequence of bytes.
* @param b the data to be written * @param b the data to be written
* @param off the start offset in the data * @param off the start offset in the data
* @param len the number of bytes that are written * @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. * @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 * Writes <code>b.length</code> bytes from the specified byte array
...@@ -276,7 +301,7 @@ class FileOutputStream extends OutputStream ...@@ -276,7 +301,7 @@ class FileOutputStream extends OutputStream
* @exception IOException if an I/O error occurs. * @exception IOException if an I/O error occurs.
*/ */
public void write(byte b[]) throws IOException { 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 ...@@ -289,7 +314,7 @@ class FileOutputStream extends OutputStream
* @exception IOException if an I/O error occurs. * @exception IOException if an I/O error occurs.
*/ */
public void write(byte b[], int off, int len) throws IOException { 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 ...@@ -372,7 +397,7 @@ class FileOutputStream extends OutputStream
public FileChannel getChannel() { public FileChannel getChannel() {
synchronized (this) { synchronized (this) {
if (channel == null) { 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() * Increment fd's use count. Invoking the channel's close()
......
...@@ -537,7 +537,11 @@ public final class ProcessBuilder ...@@ -537,7 +537,11 @@ public final class ProcessBuilder
*/ */
public File file() { return null; } 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(); throw new UnsupportedOperationException();
} }
...@@ -588,9 +592,7 @@ public final class ProcessBuilder ...@@ -588,9 +592,7 @@ public final class ProcessBuilder
public String toString() { public String toString() {
return "redirect to write to file \"" + file + "\""; return "redirect to write to file \"" + file + "\"";
} }
FileOutputStream toFileOutputStream() throws IOException { boolean append() { return false; }
return new FileOutputStream(file, false);
}
}; };
} }
...@@ -620,9 +622,7 @@ public final class ProcessBuilder ...@@ -620,9 +622,7 @@ public final class ProcessBuilder
public String toString() { public String toString() {
return "redirect to append to file \"" + file + "\""; return "redirect to append to file \"" + file + "\"";
} }
FileOutputStream toFileOutputStream() throws IOException { boolean append() { return true; }
return new FileOutputStream(file, true);
}
}; };
} }
......
...@@ -39,13 +39,12 @@ import sun.security.action.GetPropertyAction; ...@@ -39,13 +39,12 @@ import sun.security.action.GetPropertyAction;
public class FileChannelImpl public class FileChannelImpl
extends FileChannel extends FileChannel
{ {
// Used to make native read and write calls
private static final FileDispatcher nd;
// Memory allocation size for mapping buffers // Memory allocation size for mapping buffers
private static final long allocationGranularity; private static final long allocationGranularity;
// Used to make native read and write calls
private final FileDispatcher nd;
// File descriptor // File descriptor
private final FileDescriptor fd; private final FileDescriptor fd;
...@@ -63,22 +62,29 @@ public class FileChannelImpl ...@@ -63,22 +62,29 @@ public class FileChannelImpl
private final Object positionLock = new Object(); private final Object positionLock = new Object();
private FileChannelImpl(FileDescriptor fd, boolean readable, private FileChannelImpl(FileDescriptor fd, boolean readable,
boolean writable, Object parent) boolean writable, boolean append, Object parent)
{ {
this.fd = fd; this.fd = fd;
this.readable = readable; this.readable = readable;
this.writable = writable; this.writable = writable;
this.parent = parent; this.parent = parent;
this.nd = new FileDispatcherImpl(append);
} }
// Invoked by getChannel() methods // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
// of java.io.File{Input,Output}Stream and RandomAccessFile
//
public static FileChannel open(FileDescriptor fd, public static FileChannel open(FileDescriptor fd,
boolean readable, boolean writable, boolean readable, boolean writable,
Object parent) 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 { private void ensureOpen() throws IOException {
...@@ -704,6 +710,9 @@ public class FileChannelImpl ...@@ -704,6 +710,9 @@ public class FileChannelImpl
private static class Unmapper private static class Unmapper
implements Runnable implements Runnable
{ {
// may be required to close file
private static final NativeDispatcher nd = new FileDispatcherImpl();
// keep track of mapped buffer usage // keep track of mapped buffer usage
static volatile int count; static volatile int count;
static volatile long totalSize; static volatile long totalSize;
...@@ -1119,7 +1128,6 @@ public class FileChannelImpl ...@@ -1119,7 +1128,6 @@ public class FileChannelImpl
static { static {
Util.load(); Util.load();
allocationGranularity = initIDs(); allocationGranularity = initIDs();
nd = new FileDispatcherImpl();
} }
} }
...@@ -76,13 +76,13 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv *env, ...@@ -76,13 +76,13 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv *env,
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_write(JNIEnv *env, jobject this, jint byte) { 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 JNIEXPORT void JNICALL
Java_java_io_RandomAccessFile_writeBytes(JNIEnv *env, Java_java_io_RandomAccessFile_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) { 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 JNIEXPORT jlong JNICALL
......
...@@ -127,7 +127,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes, ...@@ -127,7 +127,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
} }
void 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) // Discard the 24 high-order bits of byte. See OutputStream#write(int)
char c = (char) byte; char c = (char) byte;
jint n; jint n;
...@@ -136,7 +136,11 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) { ...@@ -136,7 +136,11 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
JNU_ThrowIOException(env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
return; return;
} }
if (append == JNI_TRUE) {
n = IO_Append(fd, &c, 1);
} else {
n = IO_Write(fd, &c, 1); n = IO_Write(fd, &c, 1);
}
if (n == JVM_IO_ERR) { if (n == JVM_IO_ERR) {
JNU_ThrowIOExceptionWithLastError(env, "Write error"); JNU_ThrowIOExceptionWithLastError(env, "Write error");
} else if (n == JVM_IO_INTR) { } else if (n == JVM_IO_INTR) {
...@@ -146,7 +150,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) { ...@@ -146,7 +150,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
void void
writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
jint off, jint len, jfieldID fid) jint off, jint len, jboolean append, jfieldID fid)
{ {
jint n; jint n;
char stackBuf[BUF_SIZE]; char stackBuf[BUF_SIZE];
...@@ -185,7 +189,11 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, ...@@ -185,7 +189,11 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
JNU_ThrowIOException(env, "Stream Closed"); JNU_ThrowIOException(env, "Stream Closed");
break; break;
} }
if (append == JNI_TRUE) {
n = IO_Append(fd, buf+off, len);
} else {
n = IO_Write(fd, buf+off, len); n = IO_Write(fd, buf+off, len);
}
if (n == JVM_IO_ERR) { if (n == JVM_IO_ERR) {
JNU_ThrowIOExceptionWithLastError(env, "Write error"); JNU_ThrowIOExceptionWithLastError(env, "Write error");
break; break;
......
...@@ -41,9 +41,9 @@ extern jfieldID IO_handle_fdID; ...@@ -41,9 +41,9 @@ extern jfieldID IO_handle_fdID;
jint readSingle(JNIEnv *env, jobject this, jfieldID fid); jint readSingle(JNIEnv *env, jobject this, jfieldID fid);
jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off, jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
jint len, jfieldID fid); 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, 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 fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
void throwFileNotFoundException(JNIEnv *env, jstring path); void throwFileNotFoundException(JNIEnv *env, jstring path);
......
...@@ -111,7 +111,8 @@ final class ProcessImpl { ...@@ -111,7 +111,8 @@ final class ProcessImpl {
else if (redirects[1] == Redirect.INHERIT) else if (redirects[1] == Redirect.INHERIT)
std_fds[1] = 1; std_fds[1] = 1;
else { else {
f1 = redirects[1].toFileOutputStream(); f1 = new FileOutputStream(redirects[1].file(),
redirects[1].append());
std_fds[1] = fdAccess.get(f1.getFD()); std_fds[1] = fdAccess.get(f1.getFD());
} }
...@@ -120,7 +121,8 @@ final class ProcessImpl { ...@@ -120,7 +121,8 @@ final class ProcessImpl {
else if (redirects[2] == Redirect.INHERIT) else if (redirects[2] == Redirect.INHERIT)
std_fds[2] = 2; std_fds[2] = 2;
else { else {
f2 = redirects[2].toFileOutputStream(); f2 = new FileOutputStream(redirects[2].file(),
redirects[2].append());
std_fds[2] = fdAccess.get(f2.getFD()); std_fds[2] = fdAccess.get(f2.getFD());
} }
} }
......
...@@ -35,6 +35,13 @@ class FileDispatcherImpl extends FileDispatcher ...@@ -35,6 +35,13 @@ class FileDispatcherImpl extends FileDispatcher
init(); init();
} }
FileDispatcherImpl(boolean append) {
/* append is ignored */
}
FileDispatcherImpl() {
}
int read(FileDescriptor fd, long address, int len) throws IOException { int read(FileDescriptor fd, long address, int len) throws IOException {
return read0(fd, address, len); return read0(fd, address, len);
} }
......
...@@ -60,14 +60,14 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, ...@@ -60,14 +60,14 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) { Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
writeSingle(env, this, byte, fos_fd); writeSingle(env, this, byte, append, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) { jobject this, jbyteArray bytes, jint off, jint len, jboolean append) {
writeBytes(env, this, bytes, off, len, fos_fd); writeBytes(env, this, bytes, off, len, append, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
......
...@@ -53,8 +53,9 @@ ...@@ -53,8 +53,9 @@
#define THIS_FD(obj) (*env)->GetIntField(env, obj, IO_fd_fdID) #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_Write JVM_Write
#define IO_Sync JVM_Sync #define IO_Sync JVM_Sync
#define IO_Read JVM_Read #define IO_Read JVM_Read
......
...@@ -35,6 +35,8 @@ import java.io.FileDescriptor; ...@@ -35,6 +35,8 @@ import java.io.FileDescriptor;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.lang.ProcessBuilder.Redirect; import java.lang.ProcessBuilder.Redirect;
import java.security.AccessController;
import java.security.PrivilegedAction;
/* This class is for the exclusive use of ProcessBuilder.start() to /* This class is for the exclusive use of ProcessBuilder.start() to
* create new processes. * create new processes.
...@@ -47,6 +49,35 @@ final class ProcessImpl extends Process { ...@@ -47,6 +49,35 @@ final class ProcessImpl extends Process {
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); = 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() // System-dependent portion of ProcessBuilder.start()
static Process start(String cmdarray[], static Process start(String cmdarray[],
java.util.Map<String,String> environment, java.util.Map<String,String> environment,
...@@ -82,7 +113,8 @@ final class ProcessImpl extends Process { ...@@ -82,7 +113,8 @@ final class ProcessImpl extends Process {
else if (redirects[1] == Redirect.INHERIT) else if (redirects[1] == Redirect.INHERIT)
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
else { else {
f1 = redirects[1].toFileOutputStream(); f1 = newFileOutputStream(redirects[1].file(),
redirects[1].append());
stdHandles[1] = fdAccess.getHandle(f1.getFD()); stdHandles[1] = fdAccess.getHandle(f1.getFD());
} }
...@@ -91,7 +123,8 @@ final class ProcessImpl extends Process { ...@@ -91,7 +123,8 @@ final class ProcessImpl extends Process {
else if (redirects[2] == Redirect.INHERIT) else if (redirects[2] == Redirect.INHERIT)
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
else { else {
f2 = redirects[2].toFileOutputStream(); f2 = newFileOutputStream(redirects[2].file(),
redirects[2].append());
stdHandles[2] = fdAccess.getHandle(f2.getFD()); stdHandles[2] = fdAccess.getHandle(f2.getFD());
} }
} }
...@@ -251,5 +284,15 @@ final class ProcessImpl extends Process { ...@@ -251,5 +284,15 @@ final class ProcessImpl extends Process {
boolean redirectErrorStream) boolean redirectErrorStream)
throws IOException; 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); private static native boolean closeHandle(long handle);
} }
...@@ -35,6 +35,20 @@ class FileDispatcherImpl extends FileDispatcher ...@@ -35,6 +35,20 @@ class FileDispatcherImpl extends FileDispatcher
Util.load(); 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) int read(FileDescriptor fd, long address, int len)
throws IOException throws IOException
{ {
...@@ -54,7 +68,7 @@ class FileDispatcherImpl extends FileDispatcher ...@@ -54,7 +68,7 @@ class FileDispatcherImpl extends FileDispatcher
} }
int write(FileDescriptor fd, long address, int len) throws IOException { 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, int pwrite(FileDescriptor fd, long address, int len,
...@@ -66,7 +80,7 @@ class FileDispatcherImpl extends FileDispatcher ...@@ -66,7 +80,7 @@ class FileDispatcherImpl extends FileDispatcher
} }
long writev(FileDescriptor fd, long address, int len) throws IOException { 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 { int force(FileDescriptor fd, boolean metaData) throws IOException {
...@@ -116,13 +130,13 @@ class FileDispatcherImpl extends FileDispatcher ...@@ -116,13 +130,13 @@ class FileDispatcherImpl extends FileDispatcher
static native long readv0(FileDescriptor fd, long address, int len) static native long readv0(FileDescriptor fd, long address, int len)
throws IOException; 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; throws IOException;
static native int pwrite0(FileDescriptor fd, long address, int len, static native int pwrite0(FileDescriptor fd, long address, int len,
long position) throws IOException; 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; throws IOException;
static native int force0(FileDescriptor fd, boolean metaData) static native int force0(FileDescriptor fd, boolean metaData)
......
...@@ -157,7 +157,7 @@ class WindowsChannelFactory { ...@@ -157,7 +157,7 @@ class WindowsChannelFactory {
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor); 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 { ...@@ -230,7 +230,7 @@ class WindowsChannelFactory {
if (flags.read) if (flags.read)
dwDesiredAccess |= GENERIC_READ; dwDesiredAccess |= GENERIC_READ;
if (flags.write) if (flags.write)
dwDesiredAccess |= (flags.append) ? FILE_APPEND_DATA : GENERIC_WRITE; dwDesiredAccess |= GENERIC_WRITE;
int dwShareMode = 0; int dwShareMode = 0;
if (flags.shareRead) if (flags.shareRead)
......
...@@ -61,14 +61,15 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, ...@@ -61,14 +61,15 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) { Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
writeSingle(env, this, byte, fos_fd); writeSingle(env, this, byte, append, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env, Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) { jobject this, jbyteArray bytes, jint off, jint len, jboolean append)
writeBytes(env, this, bytes, off, len, fos_fd); {
writeBytes(env, this, bytes, off, len, append, fos_fd);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
......
...@@ -225,14 +225,7 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) { ...@@ -225,14 +225,7 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
jlong jlong
winFileHandleOpen(JNIEnv *env, jstring path, int flags) 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 = const DWORD access =
(flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
(flags & O_WRONLY) ? GENERIC_WRITE : (flags & O_WRONLY) ? GENERIC_WRITE :
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) : (flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
GENERIC_READ; GENERIC_READ;
...@@ -511,24 +504,42 @@ handleRead(jlong fd, void *buf, jint len) ...@@ -511,24 +504,42 @@ handleRead(jlong fd, void *buf, jint len)
return read; return read;
} }
JNIEXPORT static size_t writeInternal(jlong fd, const void *buf, jint len, jboolean append)
size_t
handleWrite(jlong fd, const void *buf, jint len)
{ {
BOOL result = 0; BOOL result = 0;
DWORD written = 0; DWORD written = 0;
HANDLE h = (HANDLE)fd; HANDLE h = (HANDLE)fd;
if (h != INVALID_HANDLE_VALUE) { 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 */ result = WriteFile(h, /* File handle to write */
buf, /* pointers to the buffers */ buf, /* pointers to the buffers */
len, /* number of bytes to write */ len, /* number of bytes to write */
&written, /* receives number of bytes written */ &written, /* receives number of bytes written */
NULL); /* no overlapped struct */ lpOv); /* overlapped struct */
} }
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) { if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
return -1; 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 jint
......
...@@ -41,6 +41,7 @@ JNIEXPORT int handleSync(jlong fd); ...@@ -41,6 +41,7 @@ JNIEXPORT int handleSync(jlong fd);
int handleSetLength(jlong fd, jlong length); int handleSetLength(jlong fd, jlong length);
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len); JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
JNIEXPORT size_t handleWrite(jlong fd, const 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); jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
jlong handleLseek(jlong fd, jlong offset, jint whence); jlong handleLseek(jlong fd, jlong offset, jint whence);
...@@ -74,8 +75,9 @@ jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags); ...@@ -74,8 +75,9 @@ jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags);
#define THIS_FD(obj) (*env)->GetLongField(env, obj, IO_handle_fdID) #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_Write handleWrite
#define IO_Sync handleSync #define IO_Sync handleSync
#define IO_Read handleRead #define IO_Read handleRead
......
...@@ -315,3 +315,51 @@ Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle ...@@ -315,3 +315,51 @@ Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle
{ {
return CloseHandle((HANDLE) 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);
}
...@@ -184,18 +184,28 @@ Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo ...@@ -184,18 +184,28 @@ Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo, 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; BOOL result = 0;
DWORD written = 0; DWORD written = 0;
HANDLE h = (HANDLE)(handleval(env, fdo)); HANDLE h = (HANDLE)(handleval(env, fdo));
if (h != INVALID_HANDLE_VALUE) { 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 */ result = WriteFile(h, /* File handle to write */
(LPCVOID)address, /* pointers to the buffers */ (LPCVOID)address, /* pointers to the buffers */
len, /* number of bytes to write */ len, /* number of bytes to write */
&written, /* receives number of bytes written */ &written, /* receives number of bytes written */
NULL); /* no overlapped struct */ lpOv); /* overlapped struct */
} }
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) { if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
...@@ -207,7 +217,7 @@ Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo ...@@ -207,7 +217,7 @@ Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo, 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; BOOL result = 0;
DWORD written = 0; DWORD written = 0;
...@@ -219,7 +229,16 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd ...@@ -219,7 +229,16 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd
int i = 0; int i = 0;
DWORD num = 0; DWORD num = 0;
struct iovec *iovecp = (struct iovec *)jlong_to_ptr(address); 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++) { for(i=0; i<len; i++) {
loc = (LPVOID)jlong_to_ptr(iovecp[i].iov_base); loc = (LPVOID)jlong_to_ptr(iovecp[i].iov_base);
num = iovecp[i].iov_len; num = iovecp[i].iov_len;
...@@ -227,7 +246,7 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd ...@@ -227,7 +246,7 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd
loc, /* pointers to the buffers */ loc, /* pointers to the buffers */
num, /* number of bytes to write */ num, /* number of bytes to write */
&written,/* receives number of bytes written */ &written,/* receives number of bytes written */
NULL); /* no overlapped struct */ lpOv); /* overlapped struct */
if (written > 0) { if (written > 0) {
totalWritten += written; totalWritten += written;
} }
...@@ -444,9 +463,10 @@ Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz, ...@@ -444,9 +463,10 @@ Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz,
} }
JNIEXPORT jlong JNICALL 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 hProcess = GetCurrentProcess();
HANDLE hFile = jlong_to_ptr(handle);
HANDLE hResult; HANDLE hResult;
BOOL res = DuplicateHandle(hProcess, hFile, hProcess, &hResult, 0, FALSE, BOOL res = DuplicateHandle(hProcess, hFile, hProcess, &hResult, 0, FALSE,
DUPLICATE_SAME_ACCESS); DUPLICATE_SAME_ACCESS);
......
/*
* 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();
}
}
}
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
*/ */
/* @test /* @test
* @bug 4429043 4493595 6332756 * @bug 4429043 4493595 6332756 6709457
* @summary The FileChannel file locking * @summary The FileChannel file locking
*/ */
import java.io.*; import java.io.*;
import java.nio.channels.*; import java.nio.channels.*;
import java.nio.*; import static java.nio.file.StandardOpenOption.*;
/** /**
* Testing FileChannel's lock method. * Testing FileChannel's lock method.
...@@ -55,6 +55,7 @@ public class Lock { ...@@ -55,6 +55,7 @@ public class Lock {
test2(blah, true); test2(blah, true);
test2(blah, false); test2(blah, false);
test3(blah); test3(blah);
test4(blah);
blah.delete(); blah.delete();
} }
...@@ -163,6 +164,24 @@ public class Lock { ...@@ -163,6 +164,24 @@ public class Lock {
fc1.close(); fc1.close();
fc2.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 { class MadWriter {
......
...@@ -22,14 +22,14 @@ ...@@ -22,14 +22,14 @@
*/ */
/* @test /* @test
* @bug 6191269 * @bug 6191269 6709457
* @summary Test truncate method of FileChannel * @summary Test truncate method of FileChannel
*/ */
import java.io.*; import java.io.*;
import java.nio.MappedByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import static java.nio.file.StandardOpenOption.*;
import java.util.Random; import java.util.Random;
...@@ -38,43 +38,79 @@ import java.util.Random; ...@@ -38,43 +38,79 @@ import java.util.Random;
*/ */
public class Truncate { public class Truncate {
private static final Random generator = new Random();
private static Random generator = new Random();
private static File blah;
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
blah = File.createTempFile("blah", null); File blah = File.createTempFile("blah", null);
blah.deleteOnExit(); 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++) { for(int i=0; i<100; i++) {
long testSize = generator.nextInt(1000) + 10; long testSize = generator.nextInt(1000) + 10;
initTestFile(blah, testSize); initTestFile(blah, testSize);
RandomAccessFile fis = new RandomAccessFile(blah, "rw"); FileChannel fc = (i < 50) ?
FileChannel c = fis.getChannel(); new RandomAccessFile(blah, "rw").getChannel() :
if (c.size() != testSize) FileChannel.open(blah.toPath(), READ, WRITE);
try (fc) {
if (fc.size() != testSize)
throw new RuntimeException("Size failed"); throw new RuntimeException("Size failed");
long position = generator.nextInt((int)testSize); long position = generator.nextInt((int)testSize);
c.position(position); fc.position(position);
long newSize = generator.nextInt((int)testSize); long newSize = generator.nextInt((int)testSize);
c.truncate(newSize); fc.truncate(newSize);
if (c.size() != newSize) if (fc.size() != newSize)
throw new RuntimeException("Truncate failed"); throw new RuntimeException("Truncate failed");
if (position > newSize) { if (position > newSize) {
if (c.position() != newSize) if (fc.position() != newSize)
throw new RuntimeException("Position greater than size"); throw new RuntimeException("Position greater than size");
} else { } else {
if (c.position() != position) if (fc.position() != position)
throw new RuntimeException("Truncate changed 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();
} }
/** /**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册