mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 19:44:41 +02:00
928 lines
30 KiB
C
928 lines
30 KiB
C
/*
|
|
* Copyright (c) 2001, 2017, 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.
|
|
*/
|
|
|
|
/* Access APIs for WinXP and above */
|
|
#ifndef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0501
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
|
|
#include "jni.h"
|
|
#include "io_util.h"
|
|
#include "jlong.h"
|
|
#include "io_util_md.h"
|
|
#include "dirent_md.h"
|
|
#include "java_io_FileSystem.h"
|
|
|
|
#define MAX_PATH_LENGTH 1024
|
|
|
|
static struct {
|
|
jfieldID path;
|
|
} ids;
|
|
|
|
/**
|
|
* GetFinalPathNameByHandle is available on Windows Vista and newer
|
|
*/
|
|
typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);
|
|
static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_java_io_WinNTFileSystem_initIDs(JNIEnv *env, jclass cls)
|
|
{
|
|
HMODULE handle;
|
|
jclass fileClass;
|
|
|
|
fileClass = (*env)->FindClass(env, "java/io/File");
|
|
CHECK_NULL(fileClass);
|
|
ids.path = (*env)->GetFieldID(env, fileClass, "path", "Ljava/lang/String;");
|
|
CHECK_NULL(ids.path);
|
|
|
|
// GetFinalPathNameByHandle requires Windows Vista or newer
|
|
if (GetModuleHandleExW((GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT),
|
|
(LPCWSTR)&CreateFileW, &handle) != 0)
|
|
{
|
|
GetFinalPathNameByHandle_func = (GetFinalPathNameByHandleProc)
|
|
GetProcAddress(handle, "GetFinalPathNameByHandleW");
|
|
}
|
|
}
|
|
|
|
/* -- Path operations -- */
|
|
|
|
extern int wcanonicalize(const WCHAR *path, WCHAR *out, int len);
|
|
extern int wcanonicalizeWithPrefix(const WCHAR *canonicalPrefix, const WCHAR *pathWithCanonicalPrefix, WCHAR *out, int len);
|
|
|
|
/**
|
|
* Retrieves the fully resolved (final) path for the given path or NULL
|
|
* if the function fails.
|
|
*/
|
|
static WCHAR* getFinalPath(JNIEnv *env, const WCHAR *path)
|
|
{
|
|
HANDLE h;
|
|
WCHAR *result;
|
|
DWORD error;
|
|
|
|
/* Need Windows Vista or newer to get the final path */
|
|
if (GetFinalPathNameByHandle_func == NULL)
|
|
return NULL;
|
|
|
|
h = CreateFileW(path,
|
|
FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_DELETE |
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return NULL;
|
|
|
|
/**
|
|
* Allocate a buffer for the resolved path. For a long path we may need
|
|
* to allocate a larger buffer.
|
|
*/
|
|
result = (WCHAR*)malloc(MAX_PATH * sizeof(WCHAR));
|
|
if (result != NULL) {
|
|
DWORD len = (*GetFinalPathNameByHandle_func)(h, result, MAX_PATH, 0);
|
|
if (len >= MAX_PATH) {
|
|
/* retry with a buffer of the right size */
|
|
WCHAR* newResult = (WCHAR*)realloc(result, (len+1) * sizeof(WCHAR));
|
|
if (newResult != NULL) {
|
|
result = newResult;
|
|
len = (*GetFinalPathNameByHandle_func)(h, result, len, 0);
|
|
} else {
|
|
len = 0;
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
}
|
|
}
|
|
|
|
if (len > 0) {
|
|
/**
|
|
* Strip prefix (should be \\?\ or \\?\UNC)
|
|
*/
|
|
if (result[0] == L'\\' && result[1] == L'\\' &&
|
|
result[2] == L'?' && result[3] == L'\\')
|
|
{
|
|
int isUnc = (result[4] == L'U' &&
|
|
result[5] == L'N' &&
|
|
result[6] == L'C');
|
|
int prefixLen = (isUnc) ? 7 : 4;
|
|
/* actual result length (includes terminator) */
|
|
int resultLen = len - prefixLen + (isUnc ? 1 : 0) + 1;
|
|
|
|
/* copy result without prefix into new buffer */
|
|
WCHAR *tmp = (WCHAR*)malloc(resultLen * sizeof(WCHAR));
|
|
if (tmp == NULL) {
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
len = 0;
|
|
} else {
|
|
WCHAR *p = result;
|
|
p += prefixLen;
|
|
if (isUnc) {
|
|
WCHAR *p2 = tmp;
|
|
p2[0] = L'\\';
|
|
p2++;
|
|
wcscpy(p2, p);
|
|
} else {
|
|
wcscpy(tmp, p);
|
|
}
|
|
free(result);
|
|
result = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* unable to get final path */
|
|
if (len == 0 && result != NULL) {
|
|
free(result);
|
|
result = NULL;
|
|
}
|
|
} else {
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
}
|
|
|
|
error = GetLastError();
|
|
if (CloseHandle(h))
|
|
SetLastError(error);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieves file information for the specified file. If the file is
|
|
* symbolic link then the information on fully resolved target is
|
|
* returned.
|
|
*/
|
|
static BOOL getFileInformation(const WCHAR *path,
|
|
BY_HANDLE_FILE_INFORMATION *finfo)
|
|
{
|
|
BOOL result;
|
|
DWORD error;
|
|
HANDLE h = CreateFileW(path,
|
|
FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_DELETE |
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
result = GetFileInformationByHandle(h, finfo);
|
|
error = GetLastError();
|
|
if (CloseHandle(h))
|
|
SetLastError(error);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* If the given attributes are the attributes of a reparse point, then
|
|
* read and return the attributes of the special cases.
|
|
*/
|
|
DWORD getFinalAttributesIfReparsePoint(WCHAR *path, DWORD a)
|
|
{
|
|
if ((a != INVALID_FILE_ATTRIBUTES) &&
|
|
((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION finfo;
|
|
BOOL res = getFileInformation(path, &finfo);
|
|
a = (res) ? finfo.dwFileAttributes : INVALID_FILE_ATTRIBUTES;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* Take special cases into account when retrieving the attributes
|
|
* of path
|
|
*/
|
|
DWORD getFinalAttributes(WCHAR *path)
|
|
{
|
|
DWORD attr = INVALID_FILE_ATTRIBUTES;
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA wfad;
|
|
WIN32_FIND_DATAW wfd;
|
|
HANDLE h;
|
|
|
|
if (GetFileAttributesExW(path, GetFileExInfoStandard, &wfad)) {
|
|
attr = getFinalAttributesIfReparsePoint(path, wfad.dwFileAttributes);
|
|
} else {
|
|
DWORD lerr = GetLastError();
|
|
if ((lerr == ERROR_SHARING_VIOLATION || lerr == ERROR_ACCESS_DENIED) &&
|
|
(h = FindFirstFileW(path, &wfd)) != INVALID_HANDLE_VALUE) {
|
|
attr = getFinalAttributesIfReparsePoint(path, wfd.dwFileAttributes);
|
|
FindClose(h);
|
|
}
|
|
}
|
|
return attr;
|
|
}
|
|
|
|
JNIEXPORT jstring JNICALL
|
|
Java_java_io_WinNTFileSystem_canonicalize0(JNIEnv *env, jobject this,
|
|
jstring pathname)
|
|
{
|
|
jstring rv = NULL;
|
|
WCHAR canonicalPath[MAX_PATH_LENGTH];
|
|
|
|
WITH_UNICODE_STRING(env, pathname, path) {
|
|
/* we estimate the max length of memory needed as
|
|
"currentDir. length + pathname.length"
|
|
*/
|
|
int len = (int)wcslen(path);
|
|
len += currentDirLength(path, len);
|
|
if (len > MAX_PATH_LENGTH - 1) {
|
|
WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
|
|
if (cp != NULL) {
|
|
if (wcanonicalize(path, cp, len) >= 0) {
|
|
rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
|
|
}
|
|
free(cp);
|
|
} else {
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
}
|
|
} else if (wcanonicalize(path, canonicalPath, MAX_PATH_LENGTH) >= 0) {
|
|
rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
|
|
}
|
|
} END_UNICODE_STRING(env, path);
|
|
if (rv == NULL && !(*env)->ExceptionCheck(env)) {
|
|
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
JNIEXPORT jstring JNICALL
|
|
Java_java_io_WinNTFileSystem_canonicalizeWithPrefix0(JNIEnv *env, jobject this,
|
|
jstring canonicalPrefixString,
|
|
jstring pathWithCanonicalPrefixString)
|
|
{
|
|
jstring rv = NULL;
|
|
WCHAR canonicalPath[MAX_PATH_LENGTH];
|
|
WITH_UNICODE_STRING(env, canonicalPrefixString, canonicalPrefix) {
|
|
WITH_UNICODE_STRING(env, pathWithCanonicalPrefixString, pathWithCanonicalPrefix) {
|
|
int len = (int)wcslen(canonicalPrefix) + MAX_PATH;
|
|
if (len > MAX_PATH_LENGTH) {
|
|
WCHAR *cp = (WCHAR*)malloc(len * sizeof(WCHAR));
|
|
if (cp != NULL) {
|
|
if (wcanonicalizeWithPrefix(canonicalPrefix,
|
|
pathWithCanonicalPrefix,
|
|
cp, len) >= 0) {
|
|
rv = (*env)->NewString(env, cp, (jsize)wcslen(cp));
|
|
}
|
|
free(cp);
|
|
} else {
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
}
|
|
} else if (wcanonicalizeWithPrefix(canonicalPrefix,
|
|
pathWithCanonicalPrefix,
|
|
canonicalPath, MAX_PATH_LENGTH) >= 0) {
|
|
rv = (*env)->NewString(env, canonicalPath, (jsize)wcslen(canonicalPath));
|
|
}
|
|
} END_UNICODE_STRING(env, pathWithCanonicalPrefix);
|
|
} END_UNICODE_STRING(env, canonicalPrefix);
|
|
if (rv == NULL && !(*env)->ExceptionCheck(env)) {
|
|
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/* -- Attribute accessors -- */
|
|
|
|
/* Check whether or not the file name in "path" is a Windows reserved
|
|
device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9]) based on the
|
|
returned result from GetFullPathName, which should be in thr form of
|
|
"\\.\[ReservedDeviceName]" if the path represents a reserved device
|
|
name.
|
|
Note1: GetFullPathName doesn't think "CLOCK$" (which is no longer
|
|
important anyway) is a device name, so we don't check it here.
|
|
GetFileAttributesEx will catch it later by returning 0 on NT/XP/
|
|
200X.
|
|
|
|
Note2: Theoretically the implementation could just lookup the table
|
|
below linearly if the first 4 characters of the fullpath returned
|
|
from GetFullPathName are "\\.\". The current implementation should
|
|
achieve the same result. If Microsoft add more names into their
|
|
reserved device name repository in the future, which probably will
|
|
never happen, we will need to revisit the lookup implementation.
|
|
|
|
static WCHAR* ReservedDEviceNames[] = {
|
|
L"CON", L"PRN", L"AUX", L"NUL",
|
|
L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",
|
|
L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9",
|
|
L"CLOCK$"
|
|
};
|
|
*/
|
|
|
|
static BOOL isReservedDeviceNameW(WCHAR* path) {
|
|
#define BUFSIZE 9
|
|
WCHAR buf[BUFSIZE];
|
|
WCHAR *lpf = NULL;
|
|
DWORD retLen = GetFullPathNameW(path,
|
|
BUFSIZE,
|
|
buf,
|
|
&lpf);
|
|
if ((retLen == BUFSIZE - 1 || retLen == BUFSIZE - 2) &&
|
|
buf[0] == L'\\' && buf[1] == L'\\' &&
|
|
buf[2] == L'.' && buf[3] == L'\\') {
|
|
WCHAR* dname = _wcsupr(buf + 4);
|
|
if (wcscmp(dname, L"CON") == 0 ||
|
|
wcscmp(dname, L"PRN") == 0 ||
|
|
wcscmp(dname, L"AUX") == 0 ||
|
|
wcscmp(dname, L"NUL") == 0)
|
|
return TRUE;
|
|
if ((wcsncmp(dname, L"COM", 3) == 0 ||
|
|
wcsncmp(dname, L"LPT", 3) == 0) &&
|
|
dname[3] - L'0' > 0 &&
|
|
dname[3] - L'0' <= 9)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL
|
|
Java_java_io_WinNTFileSystem_getBooleanAttributes(JNIEnv *env, jobject this,
|
|
jobject file)
|
|
{
|
|
jint rv = 0;
|
|
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return rv;
|
|
if (!isReservedDeviceNameW(pathbuf)) {
|
|
DWORD a = getFinalAttributes(pathbuf);
|
|
if (a != INVALID_FILE_ATTRIBUTES) {
|
|
rv = (java_io_FileSystem_BA_EXISTS
|
|
| ((a & FILE_ATTRIBUTE_DIRECTORY)
|
|
? java_io_FileSystem_BA_DIRECTORY
|
|
: java_io_FileSystem_BA_REGULAR)
|
|
| ((a & FILE_ATTRIBUTE_HIDDEN)
|
|
? java_io_FileSystem_BA_HIDDEN : 0));
|
|
}
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
|
|
JNIEXPORT jboolean
|
|
JNICALL Java_java_io_WinNTFileSystem_checkAccess(JNIEnv *env, jobject this,
|
|
jobject file, jint access)
|
|
{
|
|
DWORD attr;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return JNI_FALSE;
|
|
attr = GetFileAttributesW(pathbuf);
|
|
attr = getFinalAttributesIfReparsePoint(pathbuf, attr);
|
|
free(pathbuf);
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return JNI_FALSE;
|
|
switch (access) {
|
|
case java_io_FileSystem_ACCESS_READ:
|
|
case java_io_FileSystem_ACCESS_EXECUTE:
|
|
return JNI_TRUE;
|
|
case java_io_FileSystem_ACCESS_WRITE:
|
|
/* Read-only attribute ignored on directories */
|
|
if ((attr & FILE_ATTRIBUTE_DIRECTORY) ||
|
|
(attr & FILE_ATTRIBUTE_READONLY) == 0)
|
|
return JNI_TRUE;
|
|
else
|
|
return JNI_FALSE;
|
|
default:
|
|
assert(0);
|
|
return JNI_FALSE;
|
|
}
|
|
}
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_setPermission(JNIEnv *env, jobject this,
|
|
jobject file,
|
|
jint access,
|
|
jboolean enable,
|
|
jboolean owneronly)
|
|
{
|
|
jboolean rv = JNI_FALSE;
|
|
WCHAR *pathbuf;
|
|
DWORD a;
|
|
if (access == java_io_FileSystem_ACCESS_READ ||
|
|
access == java_io_FileSystem_ACCESS_EXECUTE) {
|
|
return enable;
|
|
}
|
|
pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return JNI_FALSE;
|
|
a = GetFileAttributesW(pathbuf);
|
|
|
|
/* if reparse point, get final target */
|
|
if ((a != INVALID_FILE_ATTRIBUTES) &&
|
|
((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
|
|
{
|
|
WCHAR *fp = getFinalPath(env, pathbuf);
|
|
if (fp == NULL) {
|
|
a = INVALID_FILE_ATTRIBUTES;
|
|
} else {
|
|
free(pathbuf);
|
|
pathbuf = fp;
|
|
a = GetFileAttributesW(pathbuf);
|
|
}
|
|
}
|
|
if ((a != INVALID_FILE_ATTRIBUTES) &&
|
|
((a & FILE_ATTRIBUTE_DIRECTORY) == 0))
|
|
{
|
|
if (enable)
|
|
a = a & ~FILE_ATTRIBUTE_READONLY;
|
|
else
|
|
a = a | FILE_ATTRIBUTE_READONLY;
|
|
if (SetFileAttributesW(pathbuf, a))
|
|
rv = JNI_TRUE;
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL
|
|
Java_java_io_WinNTFileSystem_getLastModifiedTime(JNIEnv *env, jobject this,
|
|
jobject file)
|
|
{
|
|
jlong rv = 0;
|
|
ULARGE_INTEGER modTime;
|
|
FILETIME t;
|
|
HANDLE h;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return rv;
|
|
h = CreateFileW(pathbuf,
|
|
/* Device query access */
|
|
0,
|
|
/* Share it */
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
/* No security attributes */
|
|
NULL,
|
|
/* Open existing or fail */
|
|
OPEN_EXISTING,
|
|
/* Backup semantics for directories */
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
/* No template file */
|
|
NULL);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
if (GetFileTime(h, NULL, NULL, &t)) {
|
|
modTime.LowPart = (DWORD) t.dwLowDateTime;
|
|
modTime.HighPart = (LONG) t.dwHighDateTime;
|
|
rv = modTime.QuadPart / 10000;
|
|
rv -= 11644473600000;
|
|
}
|
|
CloseHandle(h);
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL
|
|
Java_java_io_WinNTFileSystem_getLength(JNIEnv *env, jobject this, jobject file)
|
|
{
|
|
jlong rv = 0;
|
|
WIN32_FILE_ATTRIBUTE_DATA wfad;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return rv;
|
|
if (GetFileAttributesExW(pathbuf,
|
|
GetFileExInfoStandard,
|
|
&wfad)) {
|
|
if ((wfad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
|
|
rv = wfad.nFileSizeHigh * ((jlong)MAXDWORD + 1) + wfad.nFileSizeLow;
|
|
} else {
|
|
/* file is a reparse point so read attributes of final target */
|
|
BY_HANDLE_FILE_INFORMATION finfo;
|
|
if (getFileInformation(pathbuf, &finfo)) {
|
|
rv = finfo.nFileSizeHigh * ((jlong)MAXDWORD + 1) +
|
|
finfo.nFileSizeLow;
|
|
}
|
|
}
|
|
} else {
|
|
if (GetLastError() == ERROR_SHARING_VIOLATION) {
|
|
/* The error is "share violation", which means the file/dir
|
|
must exists. Try _wstati64, we know this at least works
|
|
for pagefile.sys and hiberfil.sys.
|
|
*/
|
|
struct _stati64 sb;
|
|
if (_wstati64(pathbuf, &sb) == 0) {
|
|
rv = sb.st_size;
|
|
}
|
|
}
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
/* -- File operations -- */
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
|
|
jstring path)
|
|
{
|
|
HANDLE h = NULL;
|
|
WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
|
|
if (pathbuf == NULL)
|
|
return JNI_FALSE;
|
|
if (isReservedDeviceNameW(pathbuf)) {
|
|
free(pathbuf);
|
|
return JNI_FALSE;
|
|
}
|
|
h = CreateFileW(
|
|
pathbuf, /* Wide char path name */
|
|
GENERIC_READ | GENERIC_WRITE, /* Read and write permission */
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, /* File sharing flags */
|
|
NULL, /* Security attributes */
|
|
CREATE_NEW, /* creation disposition */
|
|
FILE_ATTRIBUTE_NORMAL |
|
|
FILE_FLAG_OPEN_REPARSE_POINT, /* flags and attributes */
|
|
NULL);
|
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
DWORD error = GetLastError();
|
|
if ((error != ERROR_FILE_EXISTS) && (error != ERROR_ALREADY_EXISTS)) {
|
|
// return false rather than throwing an exception when there is
|
|
// an existing file.
|
|
DWORD a = GetFileAttributesW(pathbuf);
|
|
if (a == INVALID_FILE_ATTRIBUTES) {
|
|
SetLastError(error);
|
|
JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
|
|
}
|
|
}
|
|
free(pathbuf);
|
|
return JNI_FALSE;
|
|
}
|
|
free(pathbuf);
|
|
CloseHandle(h);
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static int
|
|
removeFileOrDirectory(const jchar *path)
|
|
{
|
|
/* Returns 0 on success */
|
|
DWORD a;
|
|
|
|
SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
|
|
a = GetFileAttributesW(path);
|
|
if (a == INVALID_FILE_ATTRIBUTES) {
|
|
return 1;
|
|
} else if (a & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return !RemoveDirectoryW(path);
|
|
} else {
|
|
return !DeleteFileW(path);
|
|
}
|
|
}
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
|
|
{
|
|
jboolean rv = JNI_FALSE;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL) {
|
|
return JNI_FALSE;
|
|
}
|
|
if (removeFileOrDirectory(pathbuf) == 0) {
|
|
rv = JNI_TRUE;
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
JNIEXPORT jobjectArray JNICALL
|
|
Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
|
|
{
|
|
WCHAR *search_path;
|
|
HANDLE handle;
|
|
WIN32_FIND_DATAW find_data;
|
|
int len, maxlen;
|
|
jobjectArray rv, old;
|
|
DWORD fattr;
|
|
jstring name;
|
|
jclass str_class;
|
|
WCHAR *pathbuf;
|
|
|
|
str_class = JNU_ClassString(env);
|
|
CHECK_NULL_RETURN(str_class, NULL);
|
|
|
|
pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return NULL;
|
|
search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
|
|
if (search_path == 0) {
|
|
free (pathbuf);
|
|
errno = ENOMEM;
|
|
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
|
|
return NULL;
|
|
}
|
|
wcscpy(search_path, pathbuf);
|
|
free(pathbuf);
|
|
fattr = GetFileAttributesW(search_path);
|
|
if (fattr == INVALID_FILE_ATTRIBUTES) {
|
|
free(search_path);
|
|
return NULL;
|
|
} else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
free(search_path);
|
|
return NULL;
|
|
}
|
|
|
|
/* Remove trailing space chars from directory name */
|
|
len = (int)wcslen(search_path);
|
|
while (search_path[len-1] == L' ') {
|
|
len--;
|
|
}
|
|
search_path[len] = 0;
|
|
|
|
/* Append "*", or possibly "\\*", to path */
|
|
if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
|
|
(search_path[1] == L':'
|
|
&& (search_path[2] == L'\0'
|
|
|| (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
|
|
/* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
|
|
wcscat(search_path, L"*");
|
|
} else {
|
|
wcscat(search_path, L"\\*");
|
|
}
|
|
|
|
/* Open handle to the first file */
|
|
handle = FindFirstFileW(search_path, &find_data);
|
|
free(search_path);
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
|
|
// error
|
|
return NULL;
|
|
} else {
|
|
// No files found - return an empty array
|
|
rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/* Allocate an initial String array */
|
|
len = 0;
|
|
maxlen = 16;
|
|
rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
|
|
if (rv == NULL) // Couldn't allocate an array
|
|
return NULL;
|
|
/* Scan the directory */
|
|
do {
|
|
if (!wcscmp(find_data.cFileName, L".")
|
|
|| !wcscmp(find_data.cFileName, L".."))
|
|
continue;
|
|
name = (*env)->NewString(env, find_data.cFileName,
|
|
(jsize)wcslen(find_data.cFileName));
|
|
if (name == NULL)
|
|
return NULL; // error;
|
|
if (len == maxlen) {
|
|
old = rv;
|
|
rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
|
|
if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0)
|
|
return NULL; // error
|
|
(*env)->DeleteLocalRef(env, old);
|
|
}
|
|
(*env)->SetObjectArrayElement(env, rv, len++, name);
|
|
(*env)->DeleteLocalRef(env, name);
|
|
|
|
} while (FindNextFileW(handle, &find_data));
|
|
|
|
if (GetLastError() != ERROR_NO_MORE_FILES)
|
|
return NULL; // error
|
|
FindClose(handle);
|
|
|
|
if (len < maxlen) {
|
|
/* Copy the final results into an appropriately-sized array */
|
|
old = rv;
|
|
rv = (*env)->NewObjectArray(env, len, str_class, NULL);
|
|
if (rv == NULL)
|
|
return NULL; /* error */
|
|
if (JNU_CopyObjectArray(env, rv, old, len) < 0)
|
|
return NULL; /* error */
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
|
|
jobject file)
|
|
{
|
|
BOOL h = FALSE;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL) {
|
|
/* Exception is pending */
|
|
return JNI_FALSE;
|
|
}
|
|
h = CreateDirectoryW(pathbuf, NULL);
|
|
free(pathbuf);
|
|
|
|
if (h == 0) {
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
|
|
jobject to)
|
|
{
|
|
|
|
jboolean rv = JNI_FALSE;
|
|
WCHAR *frompath = fileToNTPath(env, from, ids.path);
|
|
WCHAR *topath = fileToNTPath(env, to, ids.path);
|
|
if (frompath != NULL && topath != NULL && _wrename(frompath, topath) == 0) {
|
|
rv = JNI_TRUE;
|
|
}
|
|
free(frompath);
|
|
free(topath);
|
|
return rv;
|
|
}
|
|
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
|
|
jobject file, jlong time)
|
|
{
|
|
jboolean rv = JNI_FALSE;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
HANDLE h;
|
|
if (pathbuf == NULL)
|
|
return JNI_FALSE;
|
|
h = CreateFileW(pathbuf,
|
|
FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
0);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
ULARGE_INTEGER modTime;
|
|
FILETIME t;
|
|
modTime.QuadPart = (time + 11644473600000L) * 10000L;
|
|
t.dwLowDateTime = (DWORD)modTime.LowPart;
|
|
t.dwHighDateTime = (DWORD)modTime.HighPart;
|
|
if (SetFileTime(h, NULL, NULL, &t)) {
|
|
rv = JNI_TRUE;
|
|
}
|
|
CloseHandle(h);
|
|
}
|
|
free(pathbuf);
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
JNIEXPORT jboolean JNICALL
|
|
Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
|
|
jobject file)
|
|
{
|
|
jboolean rv = JNI_FALSE;
|
|
DWORD a;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
if (pathbuf == NULL)
|
|
return JNI_FALSE;
|
|
a = GetFileAttributesW(pathbuf);
|
|
|
|
/* if reparse point, get final target */
|
|
if ((a != INVALID_FILE_ATTRIBUTES) &&
|
|
((a & FILE_ATTRIBUTE_REPARSE_POINT) != 0))
|
|
{
|
|
WCHAR *fp = getFinalPath(env, pathbuf);
|
|
if (fp == NULL) {
|
|
a = INVALID_FILE_ATTRIBUTES;
|
|
} else {
|
|
free(pathbuf);
|
|
pathbuf = fp;
|
|
a = GetFileAttributesW(pathbuf);
|
|
}
|
|
}
|
|
|
|
if ((a != INVALID_FILE_ATTRIBUTES) &&
|
|
((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
|
|
if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
|
|
rv = JNI_TRUE;
|
|
}
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
/* -- Filesystem interface -- */
|
|
|
|
|
|
JNIEXPORT jobject JNICALL
|
|
Java_java_io_WinNTFileSystem_getDriveDirectory(JNIEnv *env, jobject this,
|
|
jint drive)
|
|
{
|
|
jstring ret = NULL;
|
|
jchar *p = currentDir(drive);
|
|
jchar *pf = p;
|
|
if (p == NULL) return NULL;
|
|
if (iswalpha(*p) && (p[1] == L':')) p += 2;
|
|
ret = (*env)->NewString(env, p, (jsize)wcslen(p));
|
|
free (pf);
|
|
return ret;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL
|
|
Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
|
|
{
|
|
return GetLogicalDrives();
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL
|
|
Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
|
|
jobject file, jint t)
|
|
{
|
|
WCHAR volname[MAX_PATH_LENGTH + 1];
|
|
jlong rv = 0L;
|
|
WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
|
|
|
|
if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
|
|
ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
|
|
if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
|
|
switch(t) {
|
|
case java_io_FileSystem_SPACE_TOTAL:
|
|
rv = long_to_jlong(totalSpace.QuadPart);
|
|
break;
|
|
case java_io_FileSystem_SPACE_FREE:
|
|
rv = long_to_jlong(freeSpace.QuadPart);
|
|
break;
|
|
case java_io_FileSystem_SPACE_USABLE:
|
|
rv = long_to_jlong(usableSpace.QuadPart);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(pathbuf);
|
|
return rv;
|
|
}
|
|
|
|
// pathname is expected to be either null or to contain the root
|
|
// of the path terminated by a backslash
|
|
JNIEXPORT jint JNICALL
|
|
Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv *env, jobject this,
|
|
jstring pathname)
|
|
{
|
|
BOOL res = 0;
|
|
DWORD maxComponentLength;
|
|
|
|
if (pathname == NULL) {
|
|
res = GetVolumeInformationW(NULL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&maxComponentLength,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
} else {
|
|
WITH_UNICODE_STRING(env, pathname, path) {
|
|
res = GetVolumeInformationW(path,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&maxComponentLength,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
} END_UNICODE_STRING(env, path);
|
|
}
|
|
|
|
if (res == 0) {
|
|
JNU_ThrowIOExceptionWithLastError(env,
|
|
"Could not get maximum component length");
|
|
}
|
|
|
|
return (jint)maxComponentLength;
|
|
}
|