| /* |
| * 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; |
| } |