mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8293121: (fs) Refactor UnixFileSystem copying into generic Unix, Linux, and BSD implementations
Reviewed-by: alanb
This commit is contained in:
parent
032be168b5
commit
0a4d0cee9f
11 changed files with 1051 additions and 854 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,10 +25,16 @@
|
||||||
|
|
||||||
package sun.nio.fs;
|
package sun.nio.fs;
|
||||||
|
|
||||||
import java.nio.file.*;
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import static sun.nio.fs.LinuxNativeDispatcher.*;
|
import static sun.nio.fs.LinuxNativeDispatcher.*;
|
||||||
|
import static sun.nio.fs.UnixConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linux implementation of FileSystem
|
* Linux implementation of FileSystem
|
||||||
|
@ -121,10 +127,56 @@ class LinuxFileSystem extends UnixFileSystem {
|
||||||
return getMountEntries("/etc/mtab");
|
return getMountEntries("/etc/mtab");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
FileStore getFileStore(UnixMountEntry entry) throws IOException {
|
FileStore getFileStore(UnixMountEntry entry) throws IOException {
|
||||||
return new LinuxFileStore(this, entry);
|
return new LinuxFileStore(this, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- file copying ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void bufferedCopy(int dst, int src, long address,
|
||||||
|
int size, long addressToPollForCancel)
|
||||||
|
throws UnixException
|
||||||
|
{
|
||||||
|
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
|
||||||
|
POSIX_FADV_NOREUSE | // will access only once
|
||||||
|
POSIX_FADV_WILLNEED; // will access in near future
|
||||||
|
posix_fadvise(src, 0, 0, advice);
|
||||||
|
|
||||||
|
super.bufferedCopy(dst, src, address, size, addressToPollForCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int directCopy(int dst, int src, long addressToPollForCancel)
|
||||||
|
throws UnixException
|
||||||
|
{
|
||||||
|
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
|
||||||
|
POSIX_FADV_NOREUSE | // will access only once
|
||||||
|
POSIX_FADV_WILLNEED; // will access in near future
|
||||||
|
posix_fadvise(src, 0, 0, advice);
|
||||||
|
|
||||||
|
return directCopy0(dst, src, addressToPollForCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- native methods --
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data between file descriptors {@code src} and {@code dst} using
|
||||||
|
* a platform-specific function or system call possibly having kernel
|
||||||
|
* support.
|
||||||
|
*
|
||||||
|
* @param dst destination file descriptor
|
||||||
|
* @param src source file descriptor
|
||||||
|
* @param addressToPollForCancel address to check for cancellation
|
||||||
|
* (a non-zero value written to this address indicates cancel)
|
||||||
|
*
|
||||||
|
* @return 0 on success, UNAVAILABLE if the platform function would block,
|
||||||
|
* UNSUPPORTED_CASE if the call does not work with the given
|
||||||
|
* parameters, or UNSUPPORTED if direct copying is not supported
|
||||||
|
* on this platform
|
||||||
|
*/
|
||||||
|
private static native int directCopy0(int dst, int src,
|
||||||
|
long addressToPollForCancel)
|
||||||
|
throws UnixException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,12 @@ class LinuxNativeDispatcher extends UnixNativeDispatcher {
|
||||||
*/
|
*/
|
||||||
static native void endmntent(long stream) throws UnixException;
|
static native void endmntent(long stream) throws UnixException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int posix_fadvise(int fd, off_t offset, off_t len, int advice);
|
||||||
|
*/
|
||||||
|
static native int posix_fadvise(int fd, long offset, long len, int advice)
|
||||||
|
throws UnixException;
|
||||||
|
|
||||||
// initialize
|
// initialize
|
||||||
private static native void init();
|
private static native void init();
|
||||||
|
|
||||||
|
|
94
src/java.base/linux/native/libnio/fs/LinuxFileSystem.c
Normal file
94
src/java.base/linux/native/libnio/fs/LinuxFileSystem.c
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "jni_util.h"
|
||||||
|
#include "jlong.h"
|
||||||
|
|
||||||
|
#include "nio.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "sun_nio_fs_LinuxFileSystem.h"
|
||||||
|
|
||||||
|
#define RESTARTABLE(_cmd, _result) do { \
|
||||||
|
do { \
|
||||||
|
_result = _cmd; \
|
||||||
|
} while((_result == -1) && (errno == EINTR)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static void throwUnixException(JNIEnv* env, int errnum) {
|
||||||
|
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
|
||||||
|
"(I)V", errnum);
|
||||||
|
if (x != NULL) {
|
||||||
|
(*env)->Throw(env, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all bytes from src to dst, within the kernel if possible,
|
||||||
|
// and return zero, otherwise return the appropriate status code.
|
||||||
|
//
|
||||||
|
// Return value
|
||||||
|
// 0 on success
|
||||||
|
// IOS_UNAVAILABLE if the platform function would block
|
||||||
|
// IOS_UNSUPPORTED_CASE if the call does not work with the given parameters
|
||||||
|
// IOS_UNSUPPORTED if direct copying is not supported on this platform
|
||||||
|
// IOS_THROWN if a Java exception is thrown
|
||||||
|
//
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_sun_nio_fs_LinuxFileSystem_directCopy0
|
||||||
|
(JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
|
||||||
|
{
|
||||||
|
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
||||||
|
|
||||||
|
// Transfer within the kernel
|
||||||
|
const size_t count = cancel != NULL ?
|
||||||
|
1048576 : // 1 MB to give cancellation a chance
|
||||||
|
0x7ffff000; // maximum number of bytes that sendfile() can transfer
|
||||||
|
ssize_t bytes_sent;
|
||||||
|
|
||||||
|
do {
|
||||||
|
RESTARTABLE(sendfile64(dst, src, NULL, count), bytes_sent);
|
||||||
|
if (bytes_sent < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return IOS_UNAVAILABLE;
|
||||||
|
if (errno == EINVAL || errno == ENOSYS)
|
||||||
|
return IOS_UNSUPPORTED_CASE;
|
||||||
|
throwUnixException(env, errno);
|
||||||
|
return IOS_THROWN;
|
||||||
|
}
|
||||||
|
if (cancel != NULL && *cancel != 0) {
|
||||||
|
throwUnixException(env, ECANCELED);
|
||||||
|
return IOS_THROWN;
|
||||||
|
}
|
||||||
|
} while (bytes_sent > 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -33,6 +33,7 @@
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "sun_nio_fs_LinuxNativeDispatcher.h"
|
#include "sun_nio_fs_LinuxNativeDispatcher.h"
|
||||||
|
|
||||||
|
@ -139,6 +140,13 @@ JNIEXPORT void JNICALL
|
||||||
Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
|
Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream)
|
||||||
{
|
{
|
||||||
FILE* fp = jlong_to_ptr(stream);
|
FILE* fp = jlong_to_ptr(stream);
|
||||||
/* FIXME - man page doesn't explain how errors are returned */
|
// The endmntent() function always returns 1.
|
||||||
endmntent(fp);
|
endmntent(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Java_sun_nio_fs_LinuxNativeDispatcher_posix_1fadvise(JNIEnv* env, jclass this,
|
||||||
|
jint fd, jlong offset, jlong len, jint advice)
|
||||||
|
{
|
||||||
|
return posix_fadvise64((int)fd, (off64_t)offset, (off64_t)len, (int)advice);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -25,10 +25,14 @@
|
||||||
|
|
||||||
package sun.nio.fs;
|
package sun.nio.fs;
|
||||||
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.WatchService;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,4 +108,19 @@ class BsdFileSystem extends UnixFileSystem {
|
||||||
FileStore getFileStore(UnixMountEntry entry) throws IOException {
|
FileStore getFileStore(UnixMountEntry entry) throws IOException {
|
||||||
return new BsdFileStore(this, entry);
|
return new BsdFileStore(this, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- file copying ---
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int directCopy(int dst, int src, long addressToPollForCancel)
|
||||||
|
throws UnixException
|
||||||
|
{
|
||||||
|
return directCopy0(dst, src, addressToPollForCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- native methods --
|
||||||
|
|
||||||
|
private static native int directCopy0(int dst, int src,
|
||||||
|
long addressToPollForCancel)
|
||||||
|
throws UnixException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -33,13 +33,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <sys/sendfile.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#elif defined(_ALLBSD_SOURCE)
|
|
||||||
#include <copyfile.h>
|
#include <copyfile.h>
|
||||||
#endif
|
#include "sun_nio_fs_BsdFileSystem.h"
|
||||||
#include "sun_nio_fs_UnixCopyFile.h"
|
|
||||||
|
|
||||||
#define RESTARTABLE(_cmd, _result) do { \
|
#define RESTARTABLE(_cmd, _result) do { \
|
||||||
do { \
|
do { \
|
||||||
|
@ -55,7 +50,6 @@ static void throwUnixException(JNIEnv* env, int errnum) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_ALLBSD_SOURCE)
|
|
||||||
int fcopyfile_callback(int what, int stage, copyfile_state_t state,
|
int fcopyfile_callback(int what, int stage, copyfile_state_t state,
|
||||||
const char* src, const char* dst, void* cancel)
|
const char* src, const char* dst, void* cancel)
|
||||||
{
|
{
|
||||||
|
@ -70,54 +64,6 @@ int fcopyfile_callback(int what, int stage, copyfile_state_t state,
|
||||||
}
|
}
|
||||||
return COPYFILE_CONTINUE;
|
return COPYFILE_CONTINUE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Copy via an intermediate temporary direct buffer
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_sun_nio_fs_UnixCopyFile_bufferedCopy0
|
|
||||||
(JNIEnv* env, jclass this, jint dst, jint src, jlong address,
|
|
||||||
jint transferSize, jlong cancelAddress)
|
|
||||||
{
|
|
||||||
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
|
||||||
|
|
||||||
char* buf = (char*)jlong_to_ptr(address);
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
|
|
||||||
POSIX_FADV_NOREUSE | // will access only once
|
|
||||||
POSIX_FADV_WILLNEED; // will access in near future
|
|
||||||
|
|
||||||
// ignore the return value hence any failure
|
|
||||||
posix_fadvise(src, 0, 0, advice);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
ssize_t n, pos, len;
|
|
||||||
RESTARTABLE(read((int)src, buf, transferSize), n);
|
|
||||||
if (n <= 0) {
|
|
||||||
if (n < 0)
|
|
||||||
throwUnixException(env, errno);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cancel != NULL && *cancel != 0) {
|
|
||||||
throwUnixException(env, ECANCELED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pos = 0;
|
|
||||||
len = n;
|
|
||||||
do {
|
|
||||||
char* bufp = buf;
|
|
||||||
bufp += pos;
|
|
||||||
RESTARTABLE(write((int)dst, bufp, len), n);
|
|
||||||
if (n == -1) {
|
|
||||||
throwUnixException(env, errno);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pos += n;
|
|
||||||
len -= n;
|
|
||||||
} while (len > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy all bytes from src to dst, within the kernel if possible (Linux),
|
// Copy all bytes from src to dst, within the kernel if possible (Linux),
|
||||||
// and return zero, otherwise return the appropriate status code.
|
// and return zero, otherwise return the appropriate status code.
|
||||||
|
@ -130,35 +76,11 @@ Java_sun_nio_fs_UnixCopyFile_bufferedCopy0
|
||||||
// IOS_THROWN if a Java exception is thrown
|
// IOS_THROWN if a Java exception is thrown
|
||||||
//
|
//
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_sun_nio_fs_UnixCopyFile_directCopy0
|
Java_sun_nio_fs_BsdFileSystem_directCopy0
|
||||||
(JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
|
(JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
|
||||||
{
|
{
|
||||||
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
// Transfer within the kernel
|
|
||||||
const size_t count = cancel != NULL ?
|
|
||||||
1048576 : // 1 MB to give cancellation a chance
|
|
||||||
0x7ffff000; // maximum number of bytes that sendfile() can transfer
|
|
||||||
ssize_t bytes_sent;
|
|
||||||
do {
|
|
||||||
RESTARTABLE(sendfile64(dst, src, NULL, count), bytes_sent);
|
|
||||||
if (bytes_sent < 0) {
|
|
||||||
if (errno == EAGAIN)
|
|
||||||
return IOS_UNAVAILABLE;
|
|
||||||
if (errno == EINVAL || errno == ENOSYS)
|
|
||||||
return IOS_UNSUPPORTED_CASE;
|
|
||||||
throwUnixException(env, errno);
|
|
||||||
return IOS_THROWN;
|
|
||||||
}
|
|
||||||
if (cancel != NULL && *cancel != 0) {
|
|
||||||
throwUnixException(env, ECANCELED);
|
|
||||||
return IOS_THROWN;
|
|
||||||
}
|
|
||||||
} while (bytes_sent > 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
#elif defined(_ALLBSD_SOURCE)
|
|
||||||
copyfile_state_t state;
|
copyfile_state_t state;
|
||||||
if (cancel != NULL) {
|
if (cancel != NULL) {
|
||||||
state = copyfile_state_alloc();
|
state = copyfile_state_alloc();
|
||||||
|
@ -178,7 +100,4 @@ Java_sun_nio_fs_UnixCopyFile_directCopy0
|
||||||
copyfile_state_free(state);
|
copyfile_state_free(state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
|
||||||
return IOS_UNSUPPORTED;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
|
@ -143,8 +143,8 @@ class UnixConstants {
|
||||||
static final int PREFIX_AT_REMOVEDIR = 00;
|
static final int PREFIX_AT_REMOVEDIR = 00;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// flags used with setattrlist
|
|
||||||
#ifdef _ALLBSD_SOURCE
|
#ifdef _ALLBSD_SOURCE
|
||||||
|
// flags used with setattrlist
|
||||||
static final int PREFIX_ATTR_CMN_CRTIME = ATTR_CMN_CRTIME;
|
static final int PREFIX_ATTR_CMN_CRTIME = ATTR_CMN_CRTIME;
|
||||||
static final int PREFIX_ATTR_CMN_MODTIME = ATTR_CMN_MODTIME;
|
static final int PREFIX_ATTR_CMN_MODTIME = ATTR_CMN_MODTIME;
|
||||||
static final int PREFIX_ATTR_CMN_ACCTIME = ATTR_CMN_ACCTIME;
|
static final int PREFIX_ATTR_CMN_ACCTIME = ATTR_CMN_ACCTIME;
|
||||||
|
@ -156,4 +156,11 @@ class UnixConstants {
|
||||||
static final int PREFIX_ATTR_CMN_ACCTIME = 00;
|
static final int PREFIX_ATTR_CMN_ACCTIME = 00;
|
||||||
static final int PREFIX_FSOPT_NOFOLLOW = 00;
|
static final int PREFIX_FSOPT_NOFOLLOW = 00;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
// advice flags used with posix_fadvise(2)
|
||||||
|
static final int PREFIX_POSIX_FADV_SEQUENTIAL = POSIX_FADV_SEQUENTIAL;
|
||||||
|
static final int PREFIX_POSIX_FADV_NOREUSE = POSIX_FADV_NOREUSE;
|
||||||
|
static final int PREFIX_POSIX_FADV_WILLNEED = POSIX_FADV_WILLNEED;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,743 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, 2022, 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. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sun.nio.fs;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.file.AtomicMoveNotSupportedException;
|
|
||||||
import java.nio.file.CopyOption;
|
|
||||||
import java.nio.file.DirectoryNotEmptyException;
|
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
|
||||||
import java.nio.file.LinkOption;
|
|
||||||
import java.nio.file.LinkPermission;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import jdk.internal.misc.Blocker;
|
|
||||||
import sun.nio.ch.DirectBuffer;
|
|
||||||
import sun.nio.ch.IOStatus;
|
|
||||||
import sun.nio.ch.Util;
|
|
||||||
import static sun.nio.fs.UnixNativeDispatcher.*;
|
|
||||||
import static sun.nio.fs.UnixConstants.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unix implementation of Path#copyTo and Path#moveTo methods.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class UnixCopyFile {
|
|
||||||
// minimum size of a temporary direct buffer
|
|
||||||
private static final int MIN_BUFFER_SIZE = 16384;
|
|
||||||
|
|
||||||
private UnixCopyFile() { }
|
|
||||||
|
|
||||||
// The flags that control how a file is copied or moved
|
|
||||||
private static class Flags {
|
|
||||||
boolean replaceExisting;
|
|
||||||
boolean atomicMove;
|
|
||||||
boolean followLinks;
|
|
||||||
boolean interruptible;
|
|
||||||
|
|
||||||
// the attributes to copy
|
|
||||||
boolean copyBasicAttributes;
|
|
||||||
boolean copyPosixAttributes;
|
|
||||||
boolean copyNonPosixAttributes;
|
|
||||||
|
|
||||||
// flags that indicate if we should fail if attributes cannot be copied
|
|
||||||
boolean failIfUnableToCopyBasic;
|
|
||||||
boolean failIfUnableToCopyPosix;
|
|
||||||
boolean failIfUnableToCopyNonPosix;
|
|
||||||
|
|
||||||
static Flags fromCopyOptions(CopyOption... options) {
|
|
||||||
Flags flags = new Flags();
|
|
||||||
flags.followLinks = true;
|
|
||||||
for (CopyOption option: options) {
|
|
||||||
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
|
||||||
flags.replaceExisting = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
|
||||||
flags.followLinks = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == StandardCopyOption.COPY_ATTRIBUTES) {
|
|
||||||
// copy all attributes but only fail if basic attributes
|
|
||||||
// cannot be copied
|
|
||||||
flags.copyBasicAttributes = true;
|
|
||||||
flags.copyPosixAttributes = true;
|
|
||||||
flags.copyNonPosixAttributes = true;
|
|
||||||
flags.failIfUnableToCopyBasic = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ExtendedOptions.INTERRUPTIBLE.matches(option)) {
|
|
||||||
flags.interruptible = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == null)
|
|
||||||
throw new NullPointerException();
|
|
||||||
throw new UnsupportedOperationException("Unsupported copy option");
|
|
||||||
}
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Flags fromMoveOptions(CopyOption... options) {
|
|
||||||
Flags flags = new Flags();
|
|
||||||
for (CopyOption option: options) {
|
|
||||||
if (option == StandardCopyOption.ATOMIC_MOVE) {
|
|
||||||
flags.atomicMove = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
|
||||||
flags.replaceExisting = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
|
||||||
// ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (option == null)
|
|
||||||
throw new NullPointerException();
|
|
||||||
throw new UnsupportedOperationException("Unsupported copy option");
|
|
||||||
}
|
|
||||||
|
|
||||||
// a move requires that all attributes be copied but only fail if
|
|
||||||
// the basic attributes cannot be copied
|
|
||||||
flags.copyBasicAttributes = true;
|
|
||||||
flags.copyPosixAttributes = true;
|
|
||||||
flags.copyNonPosixAttributes = true;
|
|
||||||
flags.failIfUnableToCopyBasic = true;
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy directory from source to target
|
|
||||||
private static void copyDirectory(UnixPath source,
|
|
||||||
UnixFileAttributes attrs,
|
|
||||||
UnixPath target,
|
|
||||||
Flags flags)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
mkdir(target, attrs.mode());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no attributes to copy
|
|
||||||
if (!flags.copyBasicAttributes &&
|
|
||||||
!flags.copyPosixAttributes &&
|
|
||||||
!flags.copyNonPosixAttributes) return;
|
|
||||||
|
|
||||||
// open target directory if possible (this can fail when copying a
|
|
||||||
// directory for which we don't have read access).
|
|
||||||
int dfd = -1;
|
|
||||||
try {
|
|
||||||
dfd = open(target, O_RDONLY, 0);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// access to target directory required to copy named attributes
|
|
||||||
if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) {
|
|
||||||
try { rmdir(target); } catch (UnixException ignore) { }
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean done = false;
|
|
||||||
try {
|
|
||||||
// copy owner/group/permissions
|
|
||||||
if (flags.copyPosixAttributes){
|
|
||||||
try {
|
|
||||||
if (dfd >= 0) {
|
|
||||||
fchown(dfd, attrs.uid(), attrs.gid());
|
|
||||||
fchmod(dfd, attrs.mode());
|
|
||||||
} else {
|
|
||||||
chown(target, attrs.uid(), attrs.gid());
|
|
||||||
chmod(target, attrs.mode());
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// unable to set owner/group
|
|
||||||
if (flags.failIfUnableToCopyPosix)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// copy other attributes
|
|
||||||
if (flags.copyNonPosixAttributes && (dfd >= 0)) {
|
|
||||||
int sfd = -1;
|
|
||||||
try {
|
|
||||||
sfd = open(source, O_RDONLY, 0);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (flags.failIfUnableToCopyNonPosix)
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
if (sfd >= 0) {
|
|
||||||
source.getFileSystem().copyNonPosixAttributes(sfd, dfd);
|
|
||||||
close(sfd, e -> null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// copy time stamps last
|
|
||||||
if (flags.copyBasicAttributes) {
|
|
||||||
try {
|
|
||||||
if (dfd >= 0 && futimesSupported()) {
|
|
||||||
futimes(dfd,
|
|
||||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
|
||||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
|
||||||
} else {
|
|
||||||
utimes(target,
|
|
||||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
|
||||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// unable to set times
|
|
||||||
if (flags.failIfUnableToCopyBasic)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done = true;
|
|
||||||
} finally {
|
|
||||||
if (dfd >= 0)
|
|
||||||
close(dfd, e -> null);
|
|
||||||
if (!done) {
|
|
||||||
// rollback
|
|
||||||
try { rmdir(target); } catch (UnixException ignore) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the least common multiple of two values;
|
|
||||||
// the parameters in general will be powers of two likely in the
|
|
||||||
// range [4096, 65536] so this algorithm is expected to converge
|
|
||||||
// when it is rarely called
|
|
||||||
private static long lcm(long x, long y) {
|
|
||||||
assert x > 0 && y > 0 : "Non-positive parameter";
|
|
||||||
|
|
||||||
long u = x;
|
|
||||||
long v = y;
|
|
||||||
|
|
||||||
while (u != v) {
|
|
||||||
if (u < v)
|
|
||||||
u += x;
|
|
||||||
else // u > v
|
|
||||||
v += y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate temporary direct buffer size
|
|
||||||
private static int temporaryBufferSize(UnixPath source, UnixPath target) {
|
|
||||||
int bufferSize = MIN_BUFFER_SIZE;
|
|
||||||
try {
|
|
||||||
long bss = UnixFileStoreAttributes.get(source).blockSize();
|
|
||||||
long bst = UnixFileStoreAttributes.get(target).blockSize();
|
|
||||||
if (bss > 0 && bst > 0) {
|
|
||||||
bufferSize = (int)(bss == bst ? bss : lcm(bss, bst));
|
|
||||||
}
|
|
||||||
if (bufferSize < MIN_BUFFER_SIZE) {
|
|
||||||
int factor = (MIN_BUFFER_SIZE + bufferSize - 1)/bufferSize;
|
|
||||||
bufferSize *= factor;
|
|
||||||
}
|
|
||||||
} catch (UnixException ignored) {
|
|
||||||
}
|
|
||||||
return bufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// whether direct copying is supported on this platform
|
|
||||||
private static volatile boolean directCopyNotSupported;
|
|
||||||
|
|
||||||
// copy regular file from source to target
|
|
||||||
private static void copyFile(UnixPath source,
|
|
||||||
UnixFileAttributes attrs,
|
|
||||||
UnixPath target,
|
|
||||||
Flags flags,
|
|
||||||
long addressToPollForCancel)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
int fi = -1;
|
|
||||||
try {
|
|
||||||
fi = open(source, O_RDONLY, 0);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// open new file
|
|
||||||
int fo = -1;
|
|
||||||
try {
|
|
||||||
fo = open(target,
|
|
||||||
(O_WRONLY |
|
|
||||||
O_CREAT |
|
|
||||||
O_EXCL),
|
|
||||||
attrs.mode());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set to true when file and attributes copied
|
|
||||||
boolean complete = false;
|
|
||||||
try {
|
|
||||||
boolean copied = false;
|
|
||||||
if (!directCopyNotSupported) {
|
|
||||||
// copy bytes to target using platform function
|
|
||||||
long comp = Blocker.begin();
|
|
||||||
try {
|
|
||||||
int res = directCopy0(fo, fi, addressToPollForCancel);
|
|
||||||
if (res == 0) {
|
|
||||||
copied = true;
|
|
||||||
} else if (res == IOStatus.UNSUPPORTED) {
|
|
||||||
directCopyNotSupported = true;
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source, target);
|
|
||||||
} finally {
|
|
||||||
Blocker.end(comp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!copied) {
|
|
||||||
// copy bytes to target via a temporary direct buffer
|
|
||||||
int bufferSize = temporaryBufferSize(source, target);
|
|
||||||
ByteBuffer buf = Util.getTemporaryDirectBuffer(bufferSize);
|
|
||||||
try {
|
|
||||||
long comp = Blocker.begin();
|
|
||||||
try {
|
|
||||||
bufferedCopy0(fo, fi, ((DirectBuffer)buf).address(),
|
|
||||||
bufferSize, addressToPollForCancel);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source, target);
|
|
||||||
} finally {
|
|
||||||
Blocker.end(comp);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Util.releaseTemporaryDirectBuffer(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy owner/permissions
|
|
||||||
if (flags.copyPosixAttributes) {
|
|
||||||
try {
|
|
||||||
fchown(fo, attrs.uid(), attrs.gid());
|
|
||||||
fchmod(fo, attrs.mode());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (flags.failIfUnableToCopyPosix)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// copy non POSIX attributes (depends on file system)
|
|
||||||
if (flags.copyNonPosixAttributes) {
|
|
||||||
source.getFileSystem().copyNonPosixAttributes(fi, fo);
|
|
||||||
}
|
|
||||||
// copy time attributes
|
|
||||||
if (flags.copyBasicAttributes) {
|
|
||||||
try {
|
|
||||||
if (futimesSupported()) {
|
|
||||||
futimes(fo,
|
|
||||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
|
||||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
|
||||||
} else {
|
|
||||||
utimes(target,
|
|
||||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
|
||||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (flags.failIfUnableToCopyBasic)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
complete = true;
|
|
||||||
} finally {
|
|
||||||
close(fo, e -> null);
|
|
||||||
|
|
||||||
// copy of file or attributes failed so rollback
|
|
||||||
if (!complete) {
|
|
||||||
try {
|
|
||||||
unlink(target);
|
|
||||||
} catch (UnixException ignore) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
close(fi, e -> null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy symbolic link from source to target
|
|
||||||
private static void copyLink(UnixPath source,
|
|
||||||
UnixFileAttributes attrs,
|
|
||||||
UnixPath target,
|
|
||||||
Flags flags)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
byte[] linktarget = null;
|
|
||||||
try {
|
|
||||||
linktarget = readlink(source);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
symlink(linktarget, target);
|
|
||||||
|
|
||||||
if (flags.copyPosixAttributes) {
|
|
||||||
try {
|
|
||||||
lchown(target, attrs.uid(), attrs.gid());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// ignore since link attributes not required to be copied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy special file from source to target
|
|
||||||
private static void copySpecial(UnixPath source,
|
|
||||||
UnixFileAttributes attrs,
|
|
||||||
UnixPath target,
|
|
||||||
Flags flags)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
mknod(target, attrs.mode(), attrs.rdev());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
boolean done = false;
|
|
||||||
try {
|
|
||||||
if (flags.copyPosixAttributes) {
|
|
||||||
try {
|
|
||||||
chown(target, attrs.uid(), attrs.gid());
|
|
||||||
chmod(target, attrs.mode());
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (flags.failIfUnableToCopyPosix)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flags.copyBasicAttributes) {
|
|
||||||
try {
|
|
||||||
utimes(target,
|
|
||||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
|
||||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (flags.failIfUnableToCopyBasic)
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done = true;
|
|
||||||
} finally {
|
|
||||||
if (!done) {
|
|
||||||
try { unlink(target); } catch (UnixException ignore) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// throw a DirectoryNotEmpty exception if appropriate
|
|
||||||
static void ensureEmptyDir(UnixPath dir) throws IOException {
|
|
||||||
try {
|
|
||||||
long ptr = opendir(dir);
|
|
||||||
try (UnixDirectoryStream stream =
|
|
||||||
new UnixDirectoryStream(dir, ptr, e -> true)) {
|
|
||||||
if (stream.iterator().hasNext()) {
|
|
||||||
throw new DirectoryNotEmptyException(
|
|
||||||
dir.getPathForExceptionMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (UnixException e) {
|
|
||||||
e.rethrowAsIOException(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// move file from source to target
|
|
||||||
static void move(UnixPath source, UnixPath target, CopyOption... options)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// permission check
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
SecurityManager sm = System.getSecurityManager();
|
|
||||||
if (sm != null) {
|
|
||||||
source.checkWrite();
|
|
||||||
target.checkWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate options into flags
|
|
||||||
Flags flags = Flags.fromMoveOptions(options);
|
|
||||||
|
|
||||||
// handle atomic rename case
|
|
||||||
if (flags.atomicMove) {
|
|
||||||
try {
|
|
||||||
rename(source, target);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (x.errno() == EXDEV) {
|
|
||||||
throw new AtomicMoveNotSupportedException(
|
|
||||||
source.getPathForExceptionMessage(),
|
|
||||||
target.getPathForExceptionMessage(),
|
|
||||||
x.errorString());
|
|
||||||
}
|
|
||||||
x.rethrowAsIOException(source, target);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move using rename or copy+delete
|
|
||||||
UnixFileAttributes sourceAttrs = null;
|
|
||||||
UnixFileAttributes targetAttrs = null;
|
|
||||||
|
|
||||||
// get attributes of source file (don't follow links)
|
|
||||||
try {
|
|
||||||
sourceAttrs = UnixFileAttributes.get(source, false);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get attributes of target file (don't follow links)
|
|
||||||
try {
|
|
||||||
targetAttrs = UnixFileAttributes.get(target, false);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
boolean targetExists = (targetAttrs != null);
|
|
||||||
|
|
||||||
// if the target exists:
|
|
||||||
// 1. check if source and target are the same file
|
|
||||||
// 2. throw exception if REPLACE_EXISTING option is not set
|
|
||||||
// 3. delete target if REPLACE_EXISTING option set
|
|
||||||
if (targetExists) {
|
|
||||||
if (sourceAttrs.isSameFile(targetAttrs))
|
|
||||||
return; // nothing to do as files are identical
|
|
||||||
if (!flags.replaceExisting) {
|
|
||||||
throw new FileAlreadyExistsException(
|
|
||||||
target.getPathForExceptionMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// attempt to delete target
|
|
||||||
try {
|
|
||||||
if (targetAttrs.isDirectory()) {
|
|
||||||
rmdir(target);
|
|
||||||
} else {
|
|
||||||
unlink(target);
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// target is non-empty directory that can't be replaced.
|
|
||||||
if (targetAttrs.isDirectory() &&
|
|
||||||
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotEmptyException(
|
|
||||||
target.getPathForExceptionMessage());
|
|
||||||
}
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// first try rename
|
|
||||||
try {
|
|
||||||
rename(source, target);
|
|
||||||
return;
|
|
||||||
} catch (UnixException x) {
|
|
||||||
if (x.errno() != EXDEV && x.errno() != EISDIR) {
|
|
||||||
x.rethrowAsIOException(source, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy source to target
|
|
||||||
if (sourceAttrs.isDirectory()) {
|
|
||||||
ensureEmptyDir(source);
|
|
||||||
copyDirectory(source, sourceAttrs, target, flags);
|
|
||||||
} else {
|
|
||||||
if (sourceAttrs.isSymbolicLink()) {
|
|
||||||
copyLink(source, sourceAttrs, target, flags);
|
|
||||||
} else {
|
|
||||||
if (sourceAttrs.isDevice()) {
|
|
||||||
copySpecial(source, sourceAttrs, target, flags);
|
|
||||||
} else {
|
|
||||||
copyFile(source, sourceAttrs, target, flags, 0L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete source
|
|
||||||
try {
|
|
||||||
if (sourceAttrs.isDirectory()) {
|
|
||||||
rmdir(source);
|
|
||||||
} else {
|
|
||||||
unlink(source);
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// file was copied but unable to unlink the source file so attempt
|
|
||||||
// to remove the target and throw a reasonable exception
|
|
||||||
try {
|
|
||||||
if (sourceAttrs.isDirectory()) {
|
|
||||||
rmdir(target);
|
|
||||||
} else {
|
|
||||||
unlink(target);
|
|
||||||
}
|
|
||||||
} catch (UnixException ignore) { }
|
|
||||||
|
|
||||||
if (sourceAttrs.isDirectory() &&
|
|
||||||
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotEmptyException(
|
|
||||||
source.getPathForExceptionMessage());
|
|
||||||
}
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy file from source to target
|
|
||||||
static void copy(final UnixPath source,
|
|
||||||
final UnixPath target,
|
|
||||||
CopyOption... options) throws IOException
|
|
||||||
{
|
|
||||||
// permission checks
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
SecurityManager sm = System.getSecurityManager();
|
|
||||||
if (sm != null) {
|
|
||||||
source.checkRead();
|
|
||||||
target.checkWrite();
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate options into flags
|
|
||||||
final Flags flags = Flags.fromCopyOptions(options);
|
|
||||||
|
|
||||||
UnixFileAttributes sourceAttrs = null;
|
|
||||||
UnixFileAttributes targetAttrs = null;
|
|
||||||
|
|
||||||
// get attributes of source file
|
|
||||||
try {
|
|
||||||
sourceAttrs = UnixFileAttributes.get(source, flags.followLinks);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
x.rethrowAsIOException(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if source file is symbolic link then we must check LinkPermission
|
|
||||||
if (sm != null && sourceAttrs.isSymbolicLink()) {
|
|
||||||
sm.checkPermission(new LinkPermission("symbolic"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get attributes of target file (don't follow links)
|
|
||||||
try {
|
|
||||||
targetAttrs = UnixFileAttributes.get(target, false);
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
boolean targetExists = (targetAttrs != null);
|
|
||||||
|
|
||||||
// if the target exists:
|
|
||||||
// 1. check if source and target are the same file
|
|
||||||
// 2. throw exception if REPLACE_EXISTING option is not set
|
|
||||||
// 3. try to unlink the target
|
|
||||||
if (targetExists) {
|
|
||||||
if (sourceAttrs.isSameFile(targetAttrs))
|
|
||||||
return; // nothing to do as files are identical
|
|
||||||
if (!flags.replaceExisting)
|
|
||||||
throw new FileAlreadyExistsException(
|
|
||||||
target.getPathForExceptionMessage());
|
|
||||||
try {
|
|
||||||
if (targetAttrs.isDirectory()) {
|
|
||||||
rmdir(target);
|
|
||||||
} else {
|
|
||||||
unlink(target);
|
|
||||||
}
|
|
||||||
} catch (UnixException x) {
|
|
||||||
// target is non-empty directory that can't be replaced.
|
|
||||||
if (targetAttrs.isDirectory() &&
|
|
||||||
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotEmptyException(
|
|
||||||
target.getPathForExceptionMessage());
|
|
||||||
}
|
|
||||||
x.rethrowAsIOException(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the copy
|
|
||||||
if (sourceAttrs.isDirectory()) {
|
|
||||||
copyDirectory(source, sourceAttrs, target, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sourceAttrs.isSymbolicLink()) {
|
|
||||||
copyLink(source, sourceAttrs, target, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!flags.interruptible) {
|
|
||||||
// non-interruptible file copy
|
|
||||||
copyFile(source, sourceAttrs, target, flags, 0L);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interruptible file copy
|
|
||||||
final UnixFileAttributes attrsToCopy = sourceAttrs;
|
|
||||||
Cancellable copyTask = new Cancellable() {
|
|
||||||
@Override public void implRun() throws IOException {
|
|
||||||
copyFile(source, attrsToCopy, target, flags,
|
|
||||||
addressToPollForCancel());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
Cancellable.runInterruptibly(copyTask);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
Throwable t = e.getCause();
|
|
||||||
if (t instanceof IOException)
|
|
||||||
throw (IOException)t;
|
|
||||||
throw new IOException(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- native methods --
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies data between file descriptors {@code src} and {@code dst} using
|
|
||||||
* a platform-specific function or system call possibly having kernel
|
|
||||||
* support.
|
|
||||||
*
|
|
||||||
* @param dst destination file descriptor
|
|
||||||
* @param src source file descriptor
|
|
||||||
* @param addressToPollForCancel address to check for cancellation
|
|
||||||
* (a non-zero value written to this address indicates cancel)
|
|
||||||
*
|
|
||||||
* @return 0 on success, UNAVAILABLE if the platform function would block,
|
|
||||||
* UNSUPPORTED_CASE if the call does not work with the given
|
|
||||||
* parameters, or UNSUPPORTED if direct copying is not supported
|
|
||||||
* on this platform
|
|
||||||
*/
|
|
||||||
private static native int directCopy0(int dst, int src,
|
|
||||||
long addressToPollForCancel)
|
|
||||||
throws UnixException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies data between file descriptors {@code src} and {@code dst} using
|
|
||||||
* an intermediate temporary direct buffer.
|
|
||||||
*
|
|
||||||
* @param dst destination file descriptor
|
|
||||||
* @param src source file descriptor
|
|
||||||
* @param address the address of the temporary direct buffer's array
|
|
||||||
* @param size the size of the temporary direct buffer's array
|
|
||||||
* @param addressToPollForCancel address to check for cancellation
|
|
||||||
* (a non-zero value written to this address indicates cancel)
|
|
||||||
*/
|
|
||||||
private static native void bufferedCopy0(int dst, int src, long address,
|
|
||||||
int size, long addressToPollForCancel)
|
|
||||||
throws UnixException;
|
|
||||||
|
|
||||||
static {
|
|
||||||
jdk.internal.loader.BootLoader.loadLibrary("nio");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -25,13 +25,38 @@
|
||||||
|
|
||||||
package sun.nio.fs;
|
package sun.nio.fs;
|
||||||
|
|
||||||
import java.nio.file.*;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.attribute.*;
|
import java.nio.file.AtomicMoveNotSupportedException;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.LinkPermission;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.PathMatcher;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.attribute.GroupPrincipal;
|
||||||
|
import java.nio.file.attribute.UserPrincipal;
|
||||||
|
import java.nio.file.attribute.UserPrincipalLookupService;
|
||||||
import java.nio.file.spi.FileSystemProvider;
|
import java.nio.file.spi.FileSystemProvider;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import jdk.internal.misc.Blocker;
|
||||||
|
import sun.nio.ch.DirectBuffer;
|
||||||
|
import sun.nio.ch.IOStatus;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
|
import static sun.nio.fs.UnixConstants.*;
|
||||||
|
import static sun.nio.fs.UnixNativeDispatcher.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base implementation of FileSystem for Unix-like implementations.
|
* Base implementation of FileSystem for Unix-like implementations.
|
||||||
|
@ -40,6 +65,12 @@ import sun.security.action.GetPropertyAction;
|
||||||
abstract class UnixFileSystem
|
abstract class UnixFileSystem
|
||||||
extends FileSystem
|
extends FileSystem
|
||||||
{
|
{
|
||||||
|
// minimum size of a temporary direct buffer
|
||||||
|
private static final int MIN_BUFFER_SIZE = 16384;
|
||||||
|
|
||||||
|
// whether direct copying is supported on this platform
|
||||||
|
private static volatile boolean directCopyNotSupported;
|
||||||
|
|
||||||
private final UnixFileSystemProvider provider;
|
private final UnixFileSystemProvider provider;
|
||||||
private final byte[] defaultDirectory;
|
private final byte[] defaultDirectory;
|
||||||
private final boolean needToResolveAgainstDefaultDirectory;
|
private final boolean needToResolveAgainstDefaultDirectory;
|
||||||
|
@ -355,4 +386,697 @@ abstract class UnixFileSystem
|
||||||
String normalizeJavaPath(String path) {
|
String normalizeJavaPath(String path) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unix implementation of Files#copy and Files#move methods.
|
||||||
|
|
||||||
|
// calculate the least common multiple of two values;
|
||||||
|
// the parameters in general will be powers of two likely in the
|
||||||
|
// range [4096, 65536] so this algorithm is expected to converge
|
||||||
|
// when it is rarely called
|
||||||
|
private static long lcm(long x, long y) {
|
||||||
|
assert x > 0 && y > 0 : "Non-positive parameter";
|
||||||
|
|
||||||
|
long u = x;
|
||||||
|
long v = y;
|
||||||
|
|
||||||
|
while (u != v) {
|
||||||
|
if (u < v)
|
||||||
|
u += x;
|
||||||
|
else // u > v
|
||||||
|
v += y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate temporary direct buffer size
|
||||||
|
private static int temporaryBufferSize(UnixPath source, UnixPath target) {
|
||||||
|
int bufferSize = MIN_BUFFER_SIZE;
|
||||||
|
try {
|
||||||
|
long bss = UnixFileStoreAttributes.get(source).blockSize();
|
||||||
|
long bst = UnixFileStoreAttributes.get(target).blockSize();
|
||||||
|
if (bss > 0 && bst > 0) {
|
||||||
|
bufferSize = (int)(bss == bst ? bss : lcm(bss, bst));
|
||||||
|
}
|
||||||
|
if (bufferSize < MIN_BUFFER_SIZE) {
|
||||||
|
int factor = (MIN_BUFFER_SIZE + bufferSize - 1)/bufferSize;
|
||||||
|
bufferSize *= factor;
|
||||||
|
}
|
||||||
|
} catch (UnixException ignored) {
|
||||||
|
}
|
||||||
|
return bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The flags that control how a file is copied or moved
|
||||||
|
private static class Flags {
|
||||||
|
boolean replaceExisting;
|
||||||
|
boolean atomicMove;
|
||||||
|
boolean followLinks;
|
||||||
|
boolean interruptible;
|
||||||
|
|
||||||
|
// the attributes to copy
|
||||||
|
boolean copyBasicAttributes;
|
||||||
|
boolean copyPosixAttributes;
|
||||||
|
boolean copyNonPosixAttributes;
|
||||||
|
|
||||||
|
// flags that indicate if we should fail if attributes cannot be copied
|
||||||
|
boolean failIfUnableToCopyBasic;
|
||||||
|
boolean failIfUnableToCopyPosix;
|
||||||
|
boolean failIfUnableToCopyNonPosix;
|
||||||
|
|
||||||
|
static Flags fromCopyOptions(CopyOption... options) {
|
||||||
|
Flags flags = new Flags();
|
||||||
|
flags.followLinks = true;
|
||||||
|
for (CopyOption option: options) {
|
||||||
|
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
||||||
|
flags.replaceExisting = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||||
|
flags.followLinks = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == StandardCopyOption.COPY_ATTRIBUTES) {
|
||||||
|
// copy all attributes but only fail if basic attributes
|
||||||
|
// cannot be copied
|
||||||
|
flags.copyBasicAttributes = true;
|
||||||
|
flags.copyPosixAttributes = true;
|
||||||
|
flags.copyNonPosixAttributes = true;
|
||||||
|
flags.failIfUnableToCopyBasic = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ExtendedOptions.INTERRUPTIBLE.matches(option)) {
|
||||||
|
flags.interruptible = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
throw new UnsupportedOperationException("Unsupported copy option");
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Flags fromMoveOptions(CopyOption... options) {
|
||||||
|
Flags flags = new Flags();
|
||||||
|
for (CopyOption option: options) {
|
||||||
|
if (option == StandardCopyOption.ATOMIC_MOVE) {
|
||||||
|
flags.atomicMove = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == StandardCopyOption.REPLACE_EXISTING) {
|
||||||
|
flags.replaceExisting = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||||
|
// ignore
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (option == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
throw new UnsupportedOperationException("Unsupported copy option");
|
||||||
|
}
|
||||||
|
|
||||||
|
// a move requires that all attributes be copied but only fail if
|
||||||
|
// the basic attributes cannot be copied
|
||||||
|
flags.copyBasicAttributes = true;
|
||||||
|
flags.copyPosixAttributes = true;
|
||||||
|
flags.copyNonPosixAttributes = true;
|
||||||
|
flags.failIfUnableToCopyBasic = true;
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy directory from source to target
|
||||||
|
private void copyDirectory(UnixPath source,
|
||||||
|
UnixFileAttributes attrs,
|
||||||
|
UnixPath target,
|
||||||
|
Flags flags)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
mkdir(target, attrs.mode());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no attributes to copy
|
||||||
|
if (!flags.copyBasicAttributes &&
|
||||||
|
!flags.copyPosixAttributes &&
|
||||||
|
!flags.copyNonPosixAttributes) return;
|
||||||
|
|
||||||
|
// open target directory if possible (this can fail when copying a
|
||||||
|
// directory for which we don't have read access).
|
||||||
|
int dfd = -1;
|
||||||
|
try {
|
||||||
|
dfd = open(target, O_RDONLY, 0);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// access to target directory required to copy named attributes
|
||||||
|
if (flags.copyNonPosixAttributes && flags.failIfUnableToCopyNonPosix) {
|
||||||
|
try { rmdir(target); } catch (UnixException ignore) { }
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean done = false;
|
||||||
|
try {
|
||||||
|
// copy owner/group/permissions
|
||||||
|
if (flags.copyPosixAttributes){
|
||||||
|
try {
|
||||||
|
if (dfd >= 0) {
|
||||||
|
fchown(dfd, attrs.uid(), attrs.gid());
|
||||||
|
fchmod(dfd, attrs.mode());
|
||||||
|
} else {
|
||||||
|
chown(target, attrs.uid(), attrs.gid());
|
||||||
|
chmod(target, attrs.mode());
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// unable to set owner/group
|
||||||
|
if (flags.failIfUnableToCopyPosix)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy other attributes
|
||||||
|
if (flags.copyNonPosixAttributes && (dfd >= 0)) {
|
||||||
|
int sfd = -1;
|
||||||
|
try {
|
||||||
|
sfd = open(source, O_RDONLY, 0);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (flags.failIfUnableToCopyNonPosix)
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
if (sfd >= 0) {
|
||||||
|
source.getFileSystem().copyNonPosixAttributes(sfd, dfd);
|
||||||
|
UnixNativeDispatcher.close(sfd, e -> null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy time stamps last
|
||||||
|
if (flags.copyBasicAttributes) {
|
||||||
|
try {
|
||||||
|
if (dfd >= 0 && futimesSupported()) {
|
||||||
|
futimes(dfd,
|
||||||
|
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||||
|
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||||
|
} else {
|
||||||
|
utimes(target,
|
||||||
|
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||||
|
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// unable to set times
|
||||||
|
if (flags.failIfUnableToCopyBasic)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
} finally {
|
||||||
|
if (dfd >= 0)
|
||||||
|
UnixNativeDispatcher.close(dfd, e -> null);
|
||||||
|
if (!done) {
|
||||||
|
// rollback
|
||||||
|
try { rmdir(target); } catch (UnixException ignore) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data between file descriptors {@code src} and {@code dst} using
|
||||||
|
* a platform-specific function or system call possibly having kernel
|
||||||
|
* support.
|
||||||
|
*
|
||||||
|
* @param dst destination file descriptor
|
||||||
|
* @param src source file descriptor
|
||||||
|
* @param addressToPollForCancel address to check for cancellation
|
||||||
|
* (a non-zero value written to this address indicates cancel)
|
||||||
|
*
|
||||||
|
* @return 0 on success, IOStatus.UNAVAILABLE if the platform function
|
||||||
|
* would block, IOStatus.UNSUPPORTED_CASE if the call does not
|
||||||
|
* work with the given parameters, or IOStatus.UNSUPPORTED if
|
||||||
|
* direct copying is not supported on this platform
|
||||||
|
*/
|
||||||
|
int directCopy(int dst, int src, long addressToPollForCancel)
|
||||||
|
throws UnixException
|
||||||
|
{
|
||||||
|
return IOStatus.UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies data between file descriptors {@code src} and {@code dst} using
|
||||||
|
* an intermediate temporary direct buffer.
|
||||||
|
*
|
||||||
|
* @param dst destination file descriptor
|
||||||
|
* @param src source file descriptor
|
||||||
|
* @param address the address of the temporary direct buffer's array
|
||||||
|
* @param size the size of the temporary direct buffer's array
|
||||||
|
* @param addressToPollForCancel address to check for cancellation
|
||||||
|
* (a non-zero value written to this address indicates cancel)
|
||||||
|
*/
|
||||||
|
void bufferedCopy(int dst, int src, long address,
|
||||||
|
int size, long addressToPollForCancel)
|
||||||
|
throws UnixException
|
||||||
|
{
|
||||||
|
bufferedCopy0(dst, src, address, size, addressToPollForCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy regular file from source to target
|
||||||
|
void copyFile(UnixPath source,
|
||||||
|
UnixFileAttributes attrs,
|
||||||
|
UnixPath target,
|
||||||
|
Flags flags,
|
||||||
|
long addressToPollForCancel)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
int fi = -1;
|
||||||
|
try {
|
||||||
|
fi = open(source, O_RDONLY, 0);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// open new file
|
||||||
|
int fo = -1;
|
||||||
|
try {
|
||||||
|
fo = open(target,
|
||||||
|
(O_WRONLY |
|
||||||
|
O_CREAT |
|
||||||
|
O_EXCL),
|
||||||
|
attrs.mode());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set to true when file and attributes copied
|
||||||
|
boolean complete = false;
|
||||||
|
try {
|
||||||
|
boolean copied = false;
|
||||||
|
if (!directCopyNotSupported) {
|
||||||
|
// copy bytes to target using platform function
|
||||||
|
long comp = Blocker.begin();
|
||||||
|
try {
|
||||||
|
int res = directCopy(fo, fi, addressToPollForCancel);
|
||||||
|
if (res == 0) {
|
||||||
|
copied = true;
|
||||||
|
} else if (res == IOStatus.UNSUPPORTED) {
|
||||||
|
directCopyNotSupported = true;
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source, target);
|
||||||
|
} finally {
|
||||||
|
Blocker.end(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copied) {
|
||||||
|
// copy bytes to target via a temporary direct buffer
|
||||||
|
int bufferSize = temporaryBufferSize(source, target);
|
||||||
|
ByteBuffer buf =
|
||||||
|
sun.nio.ch.Util.getTemporaryDirectBuffer(bufferSize);
|
||||||
|
try {
|
||||||
|
long comp = Blocker.begin();
|
||||||
|
try {
|
||||||
|
bufferedCopy(fo, fi, ((DirectBuffer)buf).address(),
|
||||||
|
bufferSize, addressToPollForCancel);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source, target);
|
||||||
|
} finally {
|
||||||
|
Blocker.end(comp);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
sun.nio.ch.Util.releaseTemporaryDirectBuffer(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy owner/permissions
|
||||||
|
if (flags.copyPosixAttributes) {
|
||||||
|
try {
|
||||||
|
fchown(fo, attrs.uid(), attrs.gid());
|
||||||
|
fchmod(fo, attrs.mode());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (flags.failIfUnableToCopyPosix)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy non POSIX attributes (depends on file system)
|
||||||
|
if (flags.copyNonPosixAttributes) {
|
||||||
|
source.getFileSystem().copyNonPosixAttributes(fi, fo);
|
||||||
|
}
|
||||||
|
// copy time attributes
|
||||||
|
if (flags.copyBasicAttributes) {
|
||||||
|
try {
|
||||||
|
if (futimesSupported()) {
|
||||||
|
futimes(fo,
|
||||||
|
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||||
|
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||||
|
} else {
|
||||||
|
utimes(target,
|
||||||
|
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||||
|
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (flags.failIfUnableToCopyBasic)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
} finally {
|
||||||
|
UnixNativeDispatcher.close(fo, e -> null);
|
||||||
|
|
||||||
|
// copy of file or attributes failed so rollback
|
||||||
|
if (!complete) {
|
||||||
|
try {
|
||||||
|
unlink(target);
|
||||||
|
} catch (UnixException ignore) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
UnixNativeDispatcher.close(fi, e -> null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy symbolic link from source to target
|
||||||
|
private void copyLink(UnixPath source,
|
||||||
|
UnixFileAttributes attrs,
|
||||||
|
UnixPath target,
|
||||||
|
Flags flags)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] linktarget = null;
|
||||||
|
try {
|
||||||
|
linktarget = readlink(source);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
symlink(linktarget, target);
|
||||||
|
|
||||||
|
if (flags.copyPosixAttributes) {
|
||||||
|
try {
|
||||||
|
lchown(target, attrs.uid(), attrs.gid());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// ignore since link attributes not required to be copied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy special file from source to target
|
||||||
|
private void copySpecial(UnixPath source,
|
||||||
|
UnixFileAttributes attrs,
|
||||||
|
UnixPath target,
|
||||||
|
Flags flags)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
mknod(target, attrs.mode(), attrs.rdev());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
boolean done = false;
|
||||||
|
try {
|
||||||
|
if (flags.copyPosixAttributes) {
|
||||||
|
try {
|
||||||
|
chown(target, attrs.uid(), attrs.gid());
|
||||||
|
chmod(target, attrs.mode());
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (flags.failIfUnableToCopyPosix)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags.copyBasicAttributes) {
|
||||||
|
try {
|
||||||
|
utimes(target,
|
||||||
|
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||||
|
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (flags.failIfUnableToCopyBasic)
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
} finally {
|
||||||
|
if (!done) {
|
||||||
|
try { unlink(target); } catch (UnixException ignore) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw a DirectoryNotEmpty exception if appropriate
|
||||||
|
static void ensureEmptyDir(UnixPath dir) throws IOException {
|
||||||
|
try {
|
||||||
|
long ptr = opendir(dir);
|
||||||
|
try (UnixDirectoryStream stream =
|
||||||
|
new UnixDirectoryStream(dir, ptr, e -> true)) {
|
||||||
|
if (stream.iterator().hasNext()) {
|
||||||
|
throw new DirectoryNotEmptyException(
|
||||||
|
dir.getPathForExceptionMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (UnixException e) {
|
||||||
|
e.rethrowAsIOException(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move file from source to target
|
||||||
|
void move(UnixPath source, UnixPath target, CopyOption... options)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// permission check
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
SecurityManager sm = System.getSecurityManager();
|
||||||
|
if (sm != null) {
|
||||||
|
source.checkWrite();
|
||||||
|
target.checkWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate options into flags
|
||||||
|
Flags flags = Flags.fromMoveOptions(options);
|
||||||
|
|
||||||
|
// handle atomic rename case
|
||||||
|
if (flags.atomicMove) {
|
||||||
|
try {
|
||||||
|
rename(source, target);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (x.errno() == EXDEV) {
|
||||||
|
throw new AtomicMoveNotSupportedException(
|
||||||
|
source.getPathForExceptionMessage(),
|
||||||
|
target.getPathForExceptionMessage(),
|
||||||
|
x.errorString());
|
||||||
|
}
|
||||||
|
x.rethrowAsIOException(source, target);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move using rename or copy+delete
|
||||||
|
UnixFileAttributes sourceAttrs = null;
|
||||||
|
UnixFileAttributes targetAttrs = null;
|
||||||
|
|
||||||
|
// get attributes of source file (don't follow links)
|
||||||
|
try {
|
||||||
|
sourceAttrs = UnixFileAttributes.get(source, false);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get attributes of target file (don't follow links)
|
||||||
|
try {
|
||||||
|
targetAttrs = UnixFileAttributes.get(target, false);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
boolean targetExists = (targetAttrs != null);
|
||||||
|
|
||||||
|
// if the target exists:
|
||||||
|
// 1. check if source and target are the same file
|
||||||
|
// 2. throw exception if REPLACE_EXISTING option is not set
|
||||||
|
// 3. delete target if REPLACE_EXISTING option set
|
||||||
|
if (targetExists) {
|
||||||
|
if (sourceAttrs.isSameFile(targetAttrs))
|
||||||
|
return; // nothing to do as files are identical
|
||||||
|
if (!flags.replaceExisting) {
|
||||||
|
throw new FileAlreadyExistsException(
|
||||||
|
target.getPathForExceptionMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to delete target
|
||||||
|
try {
|
||||||
|
if (targetAttrs.isDirectory()) {
|
||||||
|
rmdir(target);
|
||||||
|
} else {
|
||||||
|
unlink(target);
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// target is non-empty directory that can't be replaced.
|
||||||
|
if (targetAttrs.isDirectory() &&
|
||||||
|
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
||||||
|
{
|
||||||
|
throw new DirectoryNotEmptyException(
|
||||||
|
target.getPathForExceptionMessage());
|
||||||
|
}
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first try rename
|
||||||
|
try {
|
||||||
|
rename(source, target);
|
||||||
|
return;
|
||||||
|
} catch (UnixException x) {
|
||||||
|
if (x.errno() != EXDEV && x.errno() != EISDIR) {
|
||||||
|
x.rethrowAsIOException(source, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy source to target
|
||||||
|
if (sourceAttrs.isDirectory()) {
|
||||||
|
ensureEmptyDir(source);
|
||||||
|
copyDirectory(source, sourceAttrs, target, flags);
|
||||||
|
} else {
|
||||||
|
if (sourceAttrs.isSymbolicLink()) {
|
||||||
|
copyLink(source, sourceAttrs, target, flags);
|
||||||
|
} else {
|
||||||
|
if (sourceAttrs.isDevice()) {
|
||||||
|
copySpecial(source, sourceAttrs, target, flags);
|
||||||
|
} else {
|
||||||
|
copyFile(source, sourceAttrs, target, flags, 0L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete source
|
||||||
|
try {
|
||||||
|
if (sourceAttrs.isDirectory()) {
|
||||||
|
rmdir(source);
|
||||||
|
} else {
|
||||||
|
unlink(source);
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// file was copied but unable to unlink the source file so attempt
|
||||||
|
// to remove the target and throw a reasonable exception
|
||||||
|
try {
|
||||||
|
if (sourceAttrs.isDirectory()) {
|
||||||
|
rmdir(target);
|
||||||
|
} else {
|
||||||
|
unlink(target);
|
||||||
|
}
|
||||||
|
} catch (UnixException ignore) { }
|
||||||
|
|
||||||
|
if (sourceAttrs.isDirectory() &&
|
||||||
|
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
||||||
|
{
|
||||||
|
throw new DirectoryNotEmptyException(
|
||||||
|
source.getPathForExceptionMessage());
|
||||||
|
}
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy file from source to target
|
||||||
|
void copy(final UnixPath source,
|
||||||
|
final UnixPath target,
|
||||||
|
CopyOption... options) throws IOException
|
||||||
|
{
|
||||||
|
// permission checks
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
SecurityManager sm = System.getSecurityManager();
|
||||||
|
if (sm != null) {
|
||||||
|
source.checkRead();
|
||||||
|
target.checkWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate options into flags
|
||||||
|
final Flags flags = Flags.fromCopyOptions(options);
|
||||||
|
|
||||||
|
UnixFileAttributes sourceAttrs = null;
|
||||||
|
UnixFileAttributes targetAttrs = null;
|
||||||
|
|
||||||
|
// get attributes of source file
|
||||||
|
try {
|
||||||
|
sourceAttrs = UnixFileAttributes.get(source, flags.followLinks);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
x.rethrowAsIOException(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if source file is symbolic link then we must check LinkPermission
|
||||||
|
if (sm != null && sourceAttrs.isSymbolicLink()) {
|
||||||
|
sm.checkPermission(new LinkPermission("symbolic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get attributes of target file (don't follow links)
|
||||||
|
try {
|
||||||
|
targetAttrs = UnixFileAttributes.get(target, false);
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
boolean targetExists = (targetAttrs != null);
|
||||||
|
|
||||||
|
// if the target exists:
|
||||||
|
// 1. check if source and target are the same file
|
||||||
|
// 2. throw exception if REPLACE_EXISTING option is not set
|
||||||
|
// 3. try to unlink the target
|
||||||
|
if (targetExists) {
|
||||||
|
if (sourceAttrs.isSameFile(targetAttrs))
|
||||||
|
return; // nothing to do as files are identical
|
||||||
|
if (!flags.replaceExisting)
|
||||||
|
throw new FileAlreadyExistsException(
|
||||||
|
target.getPathForExceptionMessage());
|
||||||
|
try {
|
||||||
|
if (targetAttrs.isDirectory()) {
|
||||||
|
rmdir(target);
|
||||||
|
} else {
|
||||||
|
unlink(target);
|
||||||
|
}
|
||||||
|
} catch (UnixException x) {
|
||||||
|
// target is non-empty directory that can't be replaced.
|
||||||
|
if (targetAttrs.isDirectory() &&
|
||||||
|
(x.errno() == EEXIST || x.errno() == ENOTEMPTY))
|
||||||
|
{
|
||||||
|
throw new DirectoryNotEmptyException(
|
||||||
|
target.getPathForExceptionMessage());
|
||||||
|
}
|
||||||
|
x.rethrowAsIOException(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the copy
|
||||||
|
if (sourceAttrs.isDirectory()) {
|
||||||
|
copyDirectory(source, sourceAttrs, target, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceAttrs.isSymbolicLink()) {
|
||||||
|
copyLink(source, sourceAttrs, target, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!flags.interruptible) {
|
||||||
|
// non-interruptible file copy
|
||||||
|
copyFile(source, sourceAttrs, target, flags, 0L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interruptible file copy
|
||||||
|
final UnixFileAttributes attrsToCopy = sourceAttrs;
|
||||||
|
Cancellable copyTask = new Cancellable() {
|
||||||
|
@Override public void implRun() throws IOException {
|
||||||
|
copyFile(source, attrsToCopy, target,
|
||||||
|
flags, addressToPollForCancel());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
Cancellable.runInterruptibly(copyTask);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
Throwable t = e.getCause();
|
||||||
|
if (t instanceof IOException)
|
||||||
|
throw (IOException)t;
|
||||||
|
throw new IOException(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -- native methods --
|
||||||
|
|
||||||
|
private static native void bufferedCopy0(int dst, int src, long address,
|
||||||
|
int size, long addressToPollForCancel)
|
||||||
|
throws UnixException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,38 @@
|
||||||
|
|
||||||
package sun.nio.fs;
|
package sun.nio.fs;
|
||||||
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.nio.file.attribute.*;
|
|
||||||
import java.nio.file.spi.FileTypeDetector;
|
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.FilePermission;
|
import java.io.FilePermission;
|
||||||
import java.util.*;
|
import java.net.URI;
|
||||||
|
import java.nio.channels.AsynchronousFileChannel;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.SeekableByteChannel;
|
||||||
|
import java.nio.file.AccessMode;
|
||||||
|
import java.nio.file.CopyOption;
|
||||||
|
import java.nio.file.DirectoryNotEmptyException;
|
||||||
|
import java.nio.file.DirectoryStream;
|
||||||
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
|
import java.nio.file.FileStore;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystemAlreadyExistsException;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
|
import java.nio.file.LinkPermission;
|
||||||
|
import java.nio.file.NotDirectoryException;
|
||||||
|
import java.nio.file.NotLinkException;
|
||||||
|
import java.nio.file.OpenOption;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.ProviderMismatchException;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileAttribute;
|
||||||
|
import java.nio.file.attribute.FileAttributeView;
|
||||||
|
import java.nio.file.attribute.FileOwnerAttributeView;
|
||||||
|
import java.nio.file.attribute.PosixFileAttributes;
|
||||||
|
import java.nio.file.attribute.PosixFileAttributeView;
|
||||||
|
import java.nio.file.spi.FileTypeDetector;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import jdk.internal.util.StaticProperty;
|
import jdk.internal.util.StaticProperty;
|
||||||
import sun.nio.ch.ThreadPool;
|
import sun.nio.ch.ThreadPool;
|
||||||
|
@ -274,18 +297,18 @@ public abstract class UnixFileSystemProvider
|
||||||
public void copy(Path source, Path target, CopyOption... options)
|
public void copy(Path source, Path target, CopyOption... options)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
UnixCopyFile.copy(UnixPath.toUnixPath(source),
|
theFileSystem.copy(UnixPath.toUnixPath(source),
|
||||||
UnixPath.toUnixPath(target),
|
UnixPath.toUnixPath(target),
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void move(Path source, Path target, CopyOption... options)
|
public void move(Path source, Path target, CopyOption... options)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
UnixCopyFile.move(UnixPath.toUnixPath(source),
|
theFileSystem.move(UnixPath.toUnixPath(source),
|
||||||
UnixPath.toUnixPath(target),
|
UnixPath.toUnixPath(target),
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
88
src/java.base/unix/native/libnio/fs/UnixFileSystem.c
Normal file
88
src/java.base/unix/native/libnio/fs/UnixFileSystem.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008, 2022, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "jni_util.h"
|
||||||
|
#include "jlong.h"
|
||||||
|
|
||||||
|
#include "nio.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "sun_nio_fs_UnixFileSystem.h"
|
||||||
|
|
||||||
|
#define RESTARTABLE(_cmd, _result) do { \
|
||||||
|
do { \
|
||||||
|
_result = _cmd; \
|
||||||
|
} while((_result == -1) && (errno == EINTR)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
static void throwUnixException(JNIEnv* env, int errnum) {
|
||||||
|
jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException",
|
||||||
|
"(I)V", errnum);
|
||||||
|
if (x != NULL) {
|
||||||
|
(*env)->Throw(env, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy via an intermediate temporary direct buffer
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_sun_nio_fs_UnixFileSystem_bufferedCopy0
|
||||||
|
(JNIEnv* env, jclass this, jint dst, jint src, jlong address,
|
||||||
|
jint transferSize, jlong cancelAddress)
|
||||||
|
{
|
||||||
|
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
||||||
|
|
||||||
|
char* buf = (char*)jlong_to_ptr(address);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ssize_t n, pos, len;
|
||||||
|
RESTARTABLE(read((int)src, buf, transferSize), n);
|
||||||
|
if (n <= 0) {
|
||||||
|
if (n < 0)
|
||||||
|
throwUnixException(env, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cancel != NULL && *cancel != 0) {
|
||||||
|
throwUnixException(env, ECANCELED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos = 0;
|
||||||
|
len = n;
|
||||||
|
do {
|
||||||
|
char* bufp = buf;
|
||||||
|
bufp += pos;
|
||||||
|
RESTARTABLE(write((int)dst, bufp, len), n);
|
||||||
|
if (n == -1) {
|
||||||
|
throwUnixException(env, errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos += n;
|
||||||
|
len -= n;
|
||||||
|
} while (len > 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue