mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-20 02:17:53 +00:00
6709457: (fc) lock/tryLock() throws IOException "Access is denied" when file opened for append [win]
Reviewed-by: chegar
This commit is contained in:
parent
b6b4b5086c
commit
3111dfa081
@ -56,7 +56,15 @@ class FileOutputStream extends OutputStream
|
||||
*/
|
||||
private final FileDescriptor fd;
|
||||
|
||||
private FileChannel channel= null;
|
||||
/**
|
||||
* True if the file is opened for append.
|
||||
*/
|
||||
private final boolean append;
|
||||
|
||||
/**
|
||||
* The associated channel, initalized lazily.
|
||||
*/
|
||||
private FileChannel channel;
|
||||
|
||||
private final Object closeLock = new Object();
|
||||
private volatile boolean closed = false;
|
||||
@ -196,7 +204,9 @@ class FileOutputStream extends OutputStream
|
||||
if (name == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
fd = new FileDescriptor();
|
||||
this.fd = new FileDescriptor();
|
||||
this.append = append;
|
||||
|
||||
fd.incrementAndGetUseCount();
|
||||
open(name, append);
|
||||
}
|
||||
@ -232,7 +242,8 @@ class FileOutputStream extends OutputStream
|
||||
if (security != null) {
|
||||
security.checkWrite(fdObj);
|
||||
}
|
||||
fd = fdObj;
|
||||
this.fd = fdObj;
|
||||
this.append = false;
|
||||
|
||||
/*
|
||||
* FileDescriptor is being shared by streams.
|
||||
@ -250,6 +261,15 @@ class FileOutputStream extends OutputStream
|
||||
private native void open(String name, boolean append)
|
||||
throws FileNotFoundException;
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this file output stream.
|
||||
*
|
||||
* @param b the byte to be written.
|
||||
* @param append {@code true} if the write operation first
|
||||
* advances the position to the end of file
|
||||
*/
|
||||
private native void write(int b, boolean append) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this file output stream. Implements
|
||||
* the <code>write</code> method of <code>OutputStream</code>.
|
||||
@ -257,16 +277,21 @@ class FileOutputStream extends OutputStream
|
||||
* @param b the byte to be written.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public native void write(int b) throws IOException;
|
||||
public void write(int b) throws IOException {
|
||||
write(b, append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a sub array as a sequence of bytes.
|
||||
* @param b the data to be written
|
||||
* @param off the start offset in the data
|
||||
* @param len the number of bytes that are written
|
||||
* @param append {@code true} to first advance the position to the
|
||||
* end of file
|
||||
* @exception IOException If an I/O error has occurred.
|
||||
*/
|
||||
private native void writeBytes(byte b[], int off, int len) throws IOException;
|
||||
private native void writeBytes(byte b[], int off, int len, boolean append)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Writes <code>b.length</code> bytes from the specified byte array
|
||||
@ -276,7 +301,7 @@ class FileOutputStream extends OutputStream
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte b[]) throws IOException {
|
||||
writeBytes(b, 0, b.length);
|
||||
writeBytes(b, 0, b.length, append);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,7 +314,7 @@ class FileOutputStream extends OutputStream
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
writeBytes(b, off, len);
|
||||
writeBytes(b, off, len, append);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -372,7 +397,7 @@ class FileOutputStream extends OutputStream
|
||||
public FileChannel getChannel() {
|
||||
synchronized (this) {
|
||||
if (channel == null) {
|
||||
channel = FileChannelImpl.open(fd, false, true, this);
|
||||
channel = FileChannelImpl.open(fd, false, true, append, this);
|
||||
|
||||
/*
|
||||
* Increment fd's use count. Invoking the channel's close()
|
||||
|
||||
@ -537,7 +537,11 @@ public final class ProcessBuilder
|
||||
*/
|
||||
public File file() { return null; }
|
||||
|
||||
FileOutputStream toFileOutputStream() throws IOException {
|
||||
/**
|
||||
* When redirected to a destination file, indicates if the output
|
||||
* is to be written to the end of the file.
|
||||
*/
|
||||
boolean append() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@ -588,9 +592,7 @@ public final class ProcessBuilder
|
||||
public String toString() {
|
||||
return "redirect to write to file \"" + file + "\"";
|
||||
}
|
||||
FileOutputStream toFileOutputStream() throws IOException {
|
||||
return new FileOutputStream(file, false);
|
||||
}
|
||||
boolean append() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
@ -620,9 +622,7 @@ public final class ProcessBuilder
|
||||
public String toString() {
|
||||
return "redirect to append to file \"" + file + "\"";
|
||||
}
|
||||
FileOutputStream toFileOutputStream() throws IOException {
|
||||
return new FileOutputStream(file, true);
|
||||
}
|
||||
boolean append() { return true; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -39,13 +39,12 @@ import sun.security.action.GetPropertyAction;
|
||||
public class FileChannelImpl
|
||||
extends FileChannel
|
||||
{
|
||||
|
||||
// Used to make native read and write calls
|
||||
private static final FileDispatcher nd;
|
||||
|
||||
// Memory allocation size for mapping buffers
|
||||
private static final long allocationGranularity;
|
||||
|
||||
// Used to make native read and write calls
|
||||
private final FileDispatcher nd;
|
||||
|
||||
// File descriptor
|
||||
private final FileDescriptor fd;
|
||||
|
||||
@ -63,22 +62,29 @@ public class FileChannelImpl
|
||||
private final Object positionLock = new Object();
|
||||
|
||||
private FileChannelImpl(FileDescriptor fd, boolean readable,
|
||||
boolean writable, Object parent)
|
||||
boolean writable, boolean append, Object parent)
|
||||
{
|
||||
this.fd = fd;
|
||||
this.readable = readable;
|
||||
this.writable = writable;
|
||||
this.parent = parent;
|
||||
this.nd = new FileDispatcherImpl(append);
|
||||
}
|
||||
|
||||
// Invoked by getChannel() methods
|
||||
// of java.io.File{Input,Output}Stream and RandomAccessFile
|
||||
//
|
||||
// Used by FileInputStream.getChannel() and RandomAccessFile.getChannel()
|
||||
public static FileChannel open(FileDescriptor fd,
|
||||
boolean readable, boolean writable,
|
||||
Object parent)
|
||||
{
|
||||
return new FileChannelImpl(fd, readable, writable, parent);
|
||||
return new FileChannelImpl(fd, readable, writable, false, parent);
|
||||
}
|
||||
|
||||
// Used by FileOutputStream.getChannel
|
||||
public static FileChannel open(FileDescriptor fd,
|
||||
boolean readable, boolean writable,
|
||||
boolean append, Object parent)
|
||||
{
|
||||
return new FileChannelImpl(fd, readable, writable, append, parent);
|
||||
}
|
||||
|
||||
private void ensureOpen() throws IOException {
|
||||
@ -704,6 +710,9 @@ public class FileChannelImpl
|
||||
private static class Unmapper
|
||||
implements Runnable
|
||||
{
|
||||
// may be required to close file
|
||||
private static final NativeDispatcher nd = new FileDispatcherImpl();
|
||||
|
||||
// keep track of mapped buffer usage
|
||||
static volatile int count;
|
||||
static volatile long totalSize;
|
||||
@ -1119,7 +1128,6 @@ public class FileChannelImpl
|
||||
static {
|
||||
Util.load();
|
||||
allocationGranularity = initIDs();
|
||||
nd = new FileDispatcherImpl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -76,13 +76,13 @@ Java_java_io_RandomAccessFile_readBytes(JNIEnv *env,
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_RandomAccessFile_write(JNIEnv *env, jobject this, jint byte) {
|
||||
writeSingle(env, this, byte, raf_fd);
|
||||
writeSingle(env, this, byte, JNI_FALSE, raf_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_RandomAccessFile_writeBytes(JNIEnv *env,
|
||||
jobject this, jbyteArray bytes, jint off, jint len) {
|
||||
writeBytes(env, this, bytes, off, len, raf_fd);
|
||||
writeBytes(env, this, bytes, off, len, JNI_FALSE, raf_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
|
||||
@ -127,7 +127,7 @@ readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
|
||||
}
|
||||
|
||||
void
|
||||
writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
|
||||
writeSingle(JNIEnv *env, jobject this, jint byte, jboolean append, jfieldID fid) {
|
||||
// Discard the 24 high-order bits of byte. See OutputStream#write(int)
|
||||
char c = (char) byte;
|
||||
jint n;
|
||||
@ -136,7 +136,11 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
|
||||
JNU_ThrowIOException(env, "Stream Closed");
|
||||
return;
|
||||
}
|
||||
n = IO_Write(fd, &c, 1);
|
||||
if (append == JNI_TRUE) {
|
||||
n = IO_Append(fd, &c, 1);
|
||||
} else {
|
||||
n = IO_Write(fd, &c, 1);
|
||||
}
|
||||
if (n == JVM_IO_ERR) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "Write error");
|
||||
} else if (n == JVM_IO_INTR) {
|
||||
@ -146,7 +150,7 @@ writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid) {
|
||||
|
||||
void
|
||||
writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
|
||||
jint off, jint len, jfieldID fid)
|
||||
jint off, jint len, jboolean append, jfieldID fid)
|
||||
{
|
||||
jint n;
|
||||
char stackBuf[BUF_SIZE];
|
||||
@ -185,7 +189,11 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
|
||||
JNU_ThrowIOException(env, "Stream Closed");
|
||||
break;
|
||||
}
|
||||
n = IO_Write(fd, buf+off, len);
|
||||
if (append == JNI_TRUE) {
|
||||
n = IO_Append(fd, buf+off, len);
|
||||
} else {
|
||||
n = IO_Write(fd, buf+off, len);
|
||||
}
|
||||
if (n == JVM_IO_ERR) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "Write error");
|
||||
break;
|
||||
|
||||
@ -41,9 +41,9 @@ extern jfieldID IO_handle_fdID;
|
||||
jint readSingle(JNIEnv *env, jobject this, jfieldID fid);
|
||||
jint readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
|
||||
jint len, jfieldID fid);
|
||||
void writeSingle(JNIEnv *env, jobject this, jint byte, jfieldID fid);
|
||||
void writeSingle(JNIEnv *env, jobject this, jint byte, jboolean append, jfieldID fid);
|
||||
void writeBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off,
|
||||
jint len, jfieldID fid);
|
||||
jint len, jboolean append, jfieldID fid);
|
||||
void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
|
||||
void throwFileNotFoundException(JNIEnv *env, jstring path);
|
||||
|
||||
|
||||
@ -111,7 +111,8 @@ final class ProcessImpl {
|
||||
else if (redirects[1] == Redirect.INHERIT)
|
||||
std_fds[1] = 1;
|
||||
else {
|
||||
f1 = redirects[1].toFileOutputStream();
|
||||
f1 = new FileOutputStream(redirects[1].file(),
|
||||
redirects[1].append());
|
||||
std_fds[1] = fdAccess.get(f1.getFD());
|
||||
}
|
||||
|
||||
@ -120,7 +121,8 @@ final class ProcessImpl {
|
||||
else if (redirects[2] == Redirect.INHERIT)
|
||||
std_fds[2] = 2;
|
||||
else {
|
||||
f2 = redirects[2].toFileOutputStream();
|
||||
f2 = new FileOutputStream(redirects[2].file(),
|
||||
redirects[2].append());
|
||||
std_fds[2] = fdAccess.get(f2.getFD());
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,13 @@ class FileDispatcherImpl extends FileDispatcher
|
||||
init();
|
||||
}
|
||||
|
||||
FileDispatcherImpl(boolean append) {
|
||||
/* append is ignored */
|
||||
}
|
||||
|
||||
FileDispatcherImpl() {
|
||||
}
|
||||
|
||||
int read(FileDescriptor fd, long address, int len) throws IOException {
|
||||
return read0(fd, address, len);
|
||||
}
|
||||
|
||||
@ -60,14 +60,14 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
|
||||
writeSingle(env, this, byte, fos_fd);
|
||||
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
|
||||
writeSingle(env, this, byte, append, fos_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
|
||||
jobject this, jbyteArray bytes, jint off, jint len) {
|
||||
writeBytes(env, this, bytes, off, len, fos_fd);
|
||||
jobject this, jbyteArray bytes, jint off, jint len, jboolean append) {
|
||||
writeBytes(env, this, bytes, off, len, append, fos_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
||||
@ -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
|
||||
|
||||
@ -35,6 +35,8 @@ import java.io.FileDescriptor;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/* This class is for the exclusive use of ProcessBuilder.start() to
|
||||
* create new processes.
|
||||
@ -47,6 +49,35 @@ final class ProcessImpl extends Process {
|
||||
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
|
||||
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||
|
||||
/**
|
||||
* Open a file for writing. If {@code append} is {@code true} then the file
|
||||
* is opened for atomic append directly and a FileOutputStream constructed
|
||||
* with the resulting handle. This is because a FileOutputStream created
|
||||
* to append to a file does not open the file in a manner that guarantees
|
||||
* that writes by the child process will be atomic.
|
||||
*/
|
||||
private static FileOutputStream newFileOutputStream(File f, boolean append)
|
||||
throws IOException
|
||||
{
|
||||
if (append) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkWrite(f.getPath());
|
||||
long handle = openForAtomicAppend(f.getPath());
|
||||
final FileDescriptor fd = new FileDescriptor();
|
||||
fdAccess.setHandle(fd, handle);
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<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 @@ final class ProcessImpl extends Process {
|
||||
else if (redirects[1] == Redirect.INHERIT)
|
||||
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
|
||||
else {
|
||||
f1 = redirects[1].toFileOutputStream();
|
||||
f1 = newFileOutputStream(redirects[1].file(),
|
||||
redirects[1].append());
|
||||
stdHandles[1] = fdAccess.getHandle(f1.getFD());
|
||||
}
|
||||
|
||||
@ -91,7 +123,8 @@ final class ProcessImpl extends Process {
|
||||
else if (redirects[2] == Redirect.INHERIT)
|
||||
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
|
||||
else {
|
||||
f2 = redirects[2].toFileOutputStream();
|
||||
f2 = newFileOutputStream(redirects[2].file(),
|
||||
redirects[2].append());
|
||||
stdHandles[2] = fdAccess.getHandle(f2.getFD());
|
||||
}
|
||||
}
|
||||
@ -251,5 +284,15 @@ final class ProcessImpl extends Process {
|
||||
boolean redirectErrorStream)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Opens a file for atomic append. The file is created if it doesn't
|
||||
* already exist.
|
||||
*
|
||||
* @param file the file to open or create
|
||||
* @return the native HANDLE
|
||||
*/
|
||||
private static native long openForAtomicAppend(String path)
|
||||
throws IOException;
|
||||
|
||||
private static native boolean closeHandle(long handle);
|
||||
}
|
||||
|
||||
@ -35,6 +35,20 @@ class FileDispatcherImpl extends FileDispatcher
|
||||
Util.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the dispatcher should first advance the file position
|
||||
* to the end of file when writing.
|
||||
*/
|
||||
private final boolean append;
|
||||
|
||||
FileDispatcherImpl(boolean append) {
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
FileDispatcherImpl() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
int read(FileDescriptor fd, long address, int len)
|
||||
throws IOException
|
||||
{
|
||||
@ -54,7 +68,7 @@ class FileDispatcherImpl extends FileDispatcher
|
||||
}
|
||||
|
||||
int write(FileDescriptor fd, long address, int len) throws IOException {
|
||||
return write0(fd, address, len);
|
||||
return write0(fd, address, len, append);
|
||||
}
|
||||
|
||||
int pwrite(FileDescriptor fd, long address, int len,
|
||||
@ -66,7 +80,7 @@ class FileDispatcherImpl extends FileDispatcher
|
||||
}
|
||||
|
||||
long writev(FileDescriptor fd, long address, int len) throws IOException {
|
||||
return writev0(fd, address, len);
|
||||
return writev0(fd, address, len, append);
|
||||
}
|
||||
|
||||
int force(FileDescriptor fd, boolean metaData) throws IOException {
|
||||
@ -116,13 +130,13 @@ class FileDispatcherImpl extends FileDispatcher
|
||||
static native long readv0(FileDescriptor fd, long address, int len)
|
||||
throws IOException;
|
||||
|
||||
static native int write0(FileDescriptor fd, long address, int len)
|
||||
static native int write0(FileDescriptor fd, long address, int len, boolean append)
|
||||
throws IOException;
|
||||
|
||||
static native int pwrite0(FileDescriptor fd, long address, int len,
|
||||
long position) throws IOException;
|
||||
|
||||
static native long writev0(FileDescriptor fd, long address, int len)
|
||||
static native long writev0(FileDescriptor fd, long address, int len, boolean append)
|
||||
throws IOException;
|
||||
|
||||
static native int force0(FileDescriptor fd, boolean metaData)
|
||||
|
||||
@ -157,7 +157,7 @@ class WindowsChannelFactory {
|
||||
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
|
||||
|
||||
FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor);
|
||||
return FileChannelImpl.open(fdObj, flags.read, flags.write, null);
|
||||
return FileChannelImpl.open(fdObj, flags.read, flags.write, flags.append, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,7 +230,7 @@ class WindowsChannelFactory {
|
||||
if (flags.read)
|
||||
dwDesiredAccess |= GENERIC_READ;
|
||||
if (flags.write)
|
||||
dwDesiredAccess |= (flags.append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
dwDesiredAccess |= GENERIC_WRITE;
|
||||
|
||||
int dwShareMode = 0;
|
||||
if (flags.shareRead)
|
||||
|
||||
@ -61,14 +61,15 @@ Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
|
||||
writeSingle(env, this, byte, fos_fd);
|
||||
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte, jboolean append) {
|
||||
writeSingle(env, this, byte, append, fos_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
|
||||
jobject this, jbyteArray bytes, jint off, jint len) {
|
||||
writeBytes(env, this, bytes, off, len, fos_fd);
|
||||
jobject this, jbyteArray bytes, jint off, jint len, jboolean append)
|
||||
{
|
||||
writeBytes(env, this, bytes, off, len, append, fos_fd);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
||||
@ -225,14 +225,7 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
|
||||
jlong
|
||||
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
|
||||
{
|
||||
/* To implement O_APPEND, we use the strategy from
|
||||
http://msdn2.microsoft.com/en-us/library/aa363858.aspx
|
||||
"You can get atomic append by opening a file with
|
||||
FILE_APPEND_DATA access and _without_ FILE_WRITE_DATA access.
|
||||
If you do this then all writes will ignore the current file
|
||||
pointer and be done at the end-of file." */
|
||||
const DWORD access =
|
||||
(flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
|
||||
(flags & O_WRONLY) ? GENERIC_WRITE :
|
||||
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
|
||||
GENERIC_READ;
|
||||
@ -511,24 +504,42 @@ handleRead(jlong fd, void *buf, jint len)
|
||||
return read;
|
||||
}
|
||||
|
||||
JNIEXPORT
|
||||
size_t
|
||||
handleWrite(jlong fd, const void *buf, jint len)
|
||||
static size_t writeInternal(jlong fd, const void *buf, jint len, jboolean append)
|
||||
{
|
||||
BOOL result = 0;
|
||||
DWORD written = 0;
|
||||
HANDLE h = (HANDLE)fd;
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
result = WriteFile(h, /* File handle to write */
|
||||
buf, /* pointers to the buffers */
|
||||
len, /* number of bytes to write */
|
||||
&written, /* receives number of bytes written */
|
||||
NULL); /* no overlapped struct */
|
||||
OVERLAPPED ov;
|
||||
LPOVERLAPPED lpOv;
|
||||
if (append == JNI_TRUE) {
|
||||
ov.Offset = (DWORD)0xFFFFFFFF;
|
||||
ov.OffsetHigh = (DWORD)0xFFFFFFFF;
|
||||
ov.hEvent = NULL;
|
||||
lpOv = &ov;
|
||||
} else {
|
||||
lpOv = NULL;
|
||||
}
|
||||
result = WriteFile(h, /* File handle to write */
|
||||
buf, /* pointers to the buffers */
|
||||
len, /* number of bytes to write */
|
||||
&written, /* receives number of bytes written */
|
||||
lpOv); /* overlapped struct */
|
||||
}
|
||||
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
|
||||
return -1;
|
||||
}
|
||||
return written;
|
||||
return (size_t)written;
|
||||
}
|
||||
|
||||
JNIEXPORT
|
||||
size_t handleWrite(jlong fd, const void *buf, jint len) {
|
||||
return writeInternal(fd, buf, len, JNI_FALSE);
|
||||
}
|
||||
|
||||
JNIEXPORT
|
||||
size_t handleAppend(jlong fd, const void *buf, jint len) {
|
||||
return writeInternal(fd, buf, len, JNI_TRUE);
|
||||
}
|
||||
|
||||
jint
|
||||
|
||||
@ -41,6 +41,7 @@ JNIEXPORT int handleSync(jlong fd);
|
||||
int handleSetLength(jlong fd, jlong length);
|
||||
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
|
||||
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
|
||||
JNIEXPORT size_t handleAppend(jlong fd, const void *buf, jint len);
|
||||
jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
|
||||
jlong handleLseek(jlong fd, jlong offset, jint whence);
|
||||
|
||||
@ -74,8 +75,9 @@ jlong winFileHandleOpen(JNIEnv *env, jstring path, int flags);
|
||||
#define THIS_FD(obj) (*env)->GetLongField(env, obj, IO_handle_fdID)
|
||||
|
||||
/*
|
||||
* Route the routines away from HPI layer
|
||||
* Route the routines away from VM
|
||||
*/
|
||||
#define IO_Append handleAppend
|
||||
#define IO_Write handleWrite
|
||||
#define IO_Sync handleSync
|
||||
#define IO_Read handleRead
|
||||
|
||||
@ -315,3 +315,51 @@ Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle
|
||||
{
|
||||
return CloseHandle((HANDLE) handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the Unicode characters of a string. Fow now this
|
||||
* function doesn't handle long path names and other issues.
|
||||
*/
|
||||
static WCHAR* getPath(JNIEnv *env, jstring ps) {
|
||||
WCHAR *pathbuf = NULL;
|
||||
const jchar *chars = (*(env))->GetStringChars(env, ps, NULL);
|
||||
if (chars != NULL) {
|
||||
size_t pathlen = wcslen(chars);
|
||||
pathbuf = (WCHAR*)malloc((pathlen + 6) * sizeof(WCHAR));
|
||||
if (pathbuf == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, NULL);
|
||||
} else {
|
||||
wcscpy(pathbuf, chars);
|
||||
}
|
||||
(*env)->ReleaseStringChars(env, ps, chars);
|
||||
}
|
||||
return pathbuf;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_java_lang_ProcessImpl_openForAtomicAppend(JNIEnv *env, jclass ignored, jstring path)
|
||||
{
|
||||
const DWORD access = (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA);
|
||||
const DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
const DWORD disposition = OPEN_ALWAYS;
|
||||
const DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
HANDLE h;
|
||||
WCHAR *pathbuf = getPath(env, path);
|
||||
if (pathbuf == NULL) {
|
||||
/* Exception already pending */
|
||||
return -1;
|
||||
}
|
||||
h = CreateFileW(
|
||||
pathbuf, /* Wide char path name */
|
||||
access, /* Read and/or write permission */
|
||||
sharing, /* File sharing flags */
|
||||
NULL, /* Security attributes */
|
||||
disposition, /* creation disposition */
|
||||
flagsAndAttributes, /* flags and attributes */
|
||||
NULL);
|
||||
free(pathbuf);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
JNU_ThrowIOExceptionWithLastError(env, "CreateFileW");
|
||||
}
|
||||
return ptr_to_jlong(h);
|
||||
}
|
||||
|
||||
@ -184,18 +184,28 @@ Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo,
|
||||
jlong address, jint len)
|
||||
jlong address, jint len, jboolean append)
|
||||
{
|
||||
BOOL result = 0;
|
||||
DWORD written = 0;
|
||||
HANDLE h = (HANDLE)(handleval(env, fdo));
|
||||
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
OVERLAPPED ov;
|
||||
LPOVERLAPPED lpOv;
|
||||
if (append == JNI_TRUE) {
|
||||
ov.Offset = (DWORD)0xFFFFFFFF;
|
||||
ov.OffsetHigh = (DWORD)0xFFFFFFFF;
|
||||
ov.hEvent = NULL;
|
||||
lpOv = &ov;
|
||||
} else {
|
||||
lpOv = NULL;
|
||||
}
|
||||
result = WriteFile(h, /* File handle to write */
|
||||
(LPCVOID)address, /* pointers to the buffers */
|
||||
len, /* number of bytes to write */
|
||||
&written, /* receives number of bytes written */
|
||||
NULL); /* no overlapped struct */
|
||||
lpOv); /* overlapped struct */
|
||||
}
|
||||
|
||||
if ((h == INVALID_HANDLE_VALUE) || (result == 0)) {
|
||||
@ -207,7 +217,7 @@ Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo,
|
||||
jlong address, jint len)
|
||||
jlong address, jint len, jboolean append)
|
||||
{
|
||||
BOOL result = 0;
|
||||
DWORD written = 0;
|
||||
@ -219,7 +229,16 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd
|
||||
int i = 0;
|
||||
DWORD num = 0;
|
||||
struct iovec *iovecp = (struct iovec *)jlong_to_ptr(address);
|
||||
|
||||
OVERLAPPED ov;
|
||||
LPOVERLAPPED lpOv;
|
||||
if (append == JNI_TRUE) {
|
||||
ov.Offset = (DWORD)0xFFFFFFFF;
|
||||
ov.OffsetHigh = (DWORD)0xFFFFFFFF;
|
||||
ov.hEvent = NULL;
|
||||
lpOv = &ov;
|
||||
} else {
|
||||
lpOv = NULL;
|
||||
}
|
||||
for(i=0; i<len; i++) {
|
||||
loc = (LPVOID)jlong_to_ptr(iovecp[i].iov_base);
|
||||
num = iovecp[i].iov_len;
|
||||
@ -227,7 +246,7 @@ Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fd
|
||||
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 @@ Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz,
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_nio_ch_FileDispatcherImpl_duplicateHandle(JNIEnv *env, jclass this, jlong hFile)
|
||||
Java_sun_nio_ch_FileDispatcherImpl_duplicateHandle(JNIEnv *env, jclass this, jlong handle)
|
||||
{
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
HANDLE hFile = jlong_to_ptr(handle);
|
||||
HANDLE hResult;
|
||||
BOOL res = DuplicateHandle(hProcess, hFile, hProcess, &hResult, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
|
||||
107
jdk/test/java/nio/channels/FileChannel/AtomicAppend.java
Normal file
107
jdk/test/java/nio/channels/FileChannel/AtomicAppend.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 @@ public class Lock {
|
||||
test2(blah, true);
|
||||
test2(blah, false);
|
||||
test3(blah);
|
||||
test4(blah);
|
||||
blah.delete();
|
||||
}
|
||||
|
||||
@ -163,6 +164,24 @@ public class Lock {
|
||||
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 {
|
||||
|
||||
@ -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 @@ import java.util.Random;
|
||||
*/
|
||||
|
||||
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");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test behavior of truncate method when file is opened for append
|
||||
*/
|
||||
static void appendTest(File blah) throws Exception {
|
||||
for (int i=0; i<10; i++) {
|
||||
long testSize = generator.nextInt(1000) + 10;
|
||||
initTestFile(blah, testSize);
|
||||
FileChannel fc = (i < 5) ?
|
||||
new FileOutputStream(blah, true).getChannel() :
|
||||
FileChannel.open(blah.toPath(), APPEND);
|
||||
try (fc) {
|
||||
// truncate file
|
||||
long newSize = generator.nextInt((int)testSize);
|
||||
fc.truncate(newSize);
|
||||
if (fc.size() != newSize)
|
||||
throw new RuntimeException("Truncate failed");
|
||||
|
||||
// write one byte
|
||||
ByteBuffer buf = ByteBuffer.allocate(1);
|
||||
buf.put((byte)'x');
|
||||
buf.flip();
|
||||
fc.write(buf);
|
||||
if (fc.size() != (newSize+1))
|
||||
throw new RuntimeException("Unexpected size");
|
||||
}
|
||||
|
||||
c.close();
|
||||
fis.close();
|
||||
}
|
||||
blah.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user