<commit_before>/*
 * Copyright 2012 <NAME>
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#ifdef _WIN32

#include "win.h"
#include "generic.h"
#include "net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsHandleFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions.h"

#define ALL_COLORS (FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN)

/*
 * Marks the given result as failed, using the current value of GetLastError()
 */
void mark_failed_with_errno(JNIEnv* env, const char* message, jobject result) {
    mark_failed_with_code(env, message, GetLastError(), NULL, result);
}

int map_error_code(int error_code) {
    if (error_code == ERROR_PATH_NOT_FOUND) {
        return FAILURE_NO_SUCH_FILE;
    }
    if (error_code == ERROR_DIRECTORY) {
        return FAILURE_NOT_A_DIRECTORY;
    }
    return FAILURE_GENERIC;
}

//
// Returns 'true' if a file, given its attributes, is a Windows Symbolic Link.
//
bool is_file_symlink(DWORD dwFileAttributes, DWORD reparseTagData) {
    //
    // See https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
    //  IO_REPARSE_TAG_SYMLINK (0xA000000C)
    //
    return ((dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
        && (reparseTagData == IO_REPARSE_TAG_SYMLINK);
}

jlong lastModifiedNanos(FILETIME* time) {
    return ((jlong) time->dwHighDateTime << 32) | time->dwLowDateTime;
}

jlong lastModifiedNanos(LARGE_INTEGER* time) {
    return ((jlong) time->HighPart << 32) | time->LowPart;
}

//
// Retrieves the file attributes for the file specified by |pathStr|.
// If |followLink| is true, symbolic link targets are resolved.
//
// * Returns ERROR_SUCCESS if the file exists and file attributes can be retrieved,
// * Returns ERROR_SUCCESS with a FILE_TYPE_MISSING if the file does not exist,
// * Returns a Win32 error code in all other cases.
//
DWORD get_file_stat(wchar_t* pathStr, jboolean followLink, file_stat_t* pFileStat) {
#ifdef WINDOWS_MIN
    WIN32_FILE_ATTRIBUTE_DATA attr;
    BOOL ok = GetFileAttributesExW(pathStr, GetFileExInfoStandard, &attr);
    if (!ok) {
        DWORD error = GetLastError();
        if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND || error == ERROR_NOT_READY) {
            // Treat device with no media as missing
            pFileStat->lastModified = 0;
            pFileStat->size = 0;
            pFileStat->fileType = FILE_TYPE_MISSING;
            return ERROR_SUCCESS;
        }
        return error;
    }
    pFileStat->lastModified = lastModifiedNanos(&attr.ftLastWriteTime);
    if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        pFileStat->size = 0;
        pFileStat->fileType = FILE_TYPE_DIRECTORY;
    } else {
        pFileStat->size = ((LONG64) attr.nFileSizeHigh << 32) | attr.nFileSizeLow;
        pFileStat->fileType = FILE_TYPE_FILE;
    }
    return ERROR_SUCCESS;
#else    //WINDOWS_MIN: Windows Vista+ support for symlinks
    DWORD dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS;
    if (!followLink) {
        dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
    }
    HANDLE fileHandle = CreateFileW(
        pathStr,                                                   // lpFileName
        GENERIC_READ,                                              // dwDesiredAccess
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,    // dwShareMode
        NULL,                                                      // lpSecurityAttributes
        OPEN_EXISTING,                                             // dwCreationDisposition
        dwFlagsAndAttributes,                                      // dwFlagsAndAttributes
        NULL                                                       // hTemplateFile
    );
    if (fileHandle == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();
        if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND || error == ERROR_NOT_READY) {
            // Treat device with no media as missing
            pFileStat->lastModified = 0;
            pFileStat->size = 0;
            pFileStat->fileType = FILE_TYPE_MISSING;
            return ERROR_SUCCESS;
        }
        return error;
    }

    // This call allows retrieving almost everything except for the reparseTag
    BY_HANDLE_FILE_INFORMATION fileInfo;
    BOOL ok = GetFileInformationByHandle(fileHandle, &fileInfo);
    if (!ok) {
        DWORD error = GetLastError();
        CloseHandle(fileHandle);
        return error;
    }

    // This call allows retrieving the reparse tag
    FILE_ATTRIBUTE_TAG_INFO fileTagInfo;
    ok = GetFileInformationByHandleEx(fileHandle, FileAttributeTagInfo, &fileTagInfo, sizeof(fileTagInfo));
    if (!ok) {
        DWORD error = GetLastError();
        CloseHandle(fileHandle);
        return error;
    }

    CloseHandle(fileHandle);

    pFileStat->lastModified = lastModifiedNanos(&fileInfo.ftLastWriteTime);
    pFileStat->size = 0;
    if (is_file_symlink(fileTagInfo.FileAttributes, fileTagInfo.ReparseTag)) {
        pFileStat->fileType = FILE_TYPE_SYMLINK;
    } else if (fileTagInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        pFileStat->fileType = FILE_TYPE_DIRECTORY;
    } else {
        pFileStat->size = ((jlong) fileInfo.nFileSizeHigh << 32) | fileInfo.nFileSizeLow;
        pFileStat->fileType = FILE_TYPE_FILE;
    }
    return ERROR_SUCCESS;
#endif
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv* env, jclass target, jobject info, jobject result) {
    jclass infoClass = env->GetObjectClass(info);

    OSVERSIONINFOEX versionInfo;
    versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if (GetVersionEx((OSVERSIONINFO*) &versionInfo) == 0) {
        mark_failed_with_errno(env, "could not get version info", result);
        return;
    }

    SYSTEM_INFO systemInfo;
    GetNativeSystemInfo(&systemInfo);
    jstring arch = NULL;
    if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {<commit_msg>Fix stat on Windows FAT file systems<commit_after>/*
 * Copyright 2012 <NAME>
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#ifdef _WIN32

#include "win.h"
#include "generic.h"
#include "net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsHandleFunctions.h"
#include "net_rubygrapefruit_platform_internal_jni_WindowsRegistryFunctions.h"

#define ALL_COLORS (FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN)

/*
 * Marks the given result as failed, using the current value of GetLastError()
 */
void mark_failed_with_errno(JNIEnv* env, const char* message, jobject result) {
    mark_failed_with_code(env, message, GetLastError(), NULL, result);
}

int map_error_code(int error_code) {
    if (error_code == ERROR_PATH_NOT_FOUND) {
        return FAILURE_NO_SUCH_FILE;
    }
    if (error_code == ERROR_DIRECTORY) {
        return FAILURE_NOT_A_DIRECTORY;
    }
    return FAILURE_GENERIC;
}

//
// Returns 'true' if a file, given its attributes, is a Windows Symbolic Link.
//
bool is_file_symlink(DWORD dwFileAttributes, DWORD reparseTagData) {
    //
    // See https://docs.microsoft.com/en-us/windows/desktop/fileio/reparse-point-tags
    //  IO_REPARSE_TAG_SYMLINK (0xA000000C)
    //
    return ((dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT)
        && (reparseTagData == IO_REPARSE_TAG_SYMLINK);
}

jlong lastModifiedNanos(FILETIME* time) {
    return ((jlong) time->dwHighDateTime << 32) | time->dwLowDateTime;
}

jlong lastModifiedNanos(LARGE_INTEGER* time) {
    return ((jlong) time->HighPart << 32) | time->LowPart;
}

void fillFileStat(file_stat_t* pFileStat, bool symlink, DWORD attributes, BY_HANDLE_FILE_INFORMATION fileInfo) {
    pFileStat->lastModified = lastModifiedNanos(&fileInfo.ftLastWriteTime);
    pFileStat->size = 0;
    if (symlink) {
        pFileStat->fileType = FILE_TYPE_SYMLINK;
    } else if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
        pFileStat->fileType = FILE_TYPE_DIRECTORY;
    } else {
        pFileStat->size = ((jlong) fileInfo.nFileSizeHigh << 32) | fileInfo.nFileSizeLow;
        pFileStat->fileType = FILE_TYPE_FILE;
    }
}

//
// Retrieves the file attributes for the file specified by |pathStr|.
// If |followLink| is true, symbolic link targets are resolved.
//
// * Returns ERROR_SUCCESS if the file exists and file attributes can be retrieved,
// * Returns ERROR_SUCCESS with a FILE_TYPE_MISSING if the file does not exist,
// * Returns a Win32 error code in all other cases.
//
DWORD get_file_stat(wchar_t* pathStr, jboolean followLink, file_stat_t* pFileStat) {
#ifdef WINDOWS_MIN
    WIN32_FILE_ATTRIBUTE_DATA attr;
    BOOL ok = GetFileAttributesExW(pathStr, GetFileExInfoStandard, &attr);
    if (!ok) {
        DWORD error = GetLastError();
        if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND || error == ERROR_NOT_READY) {
            // Treat device with no media as missing
            pFileStat->lastModified = 0;
            pFileStat->size = 0;
            pFileStat->fileType = FILE_TYPE_MISSING;
            return ERROR_SUCCESS;
        }
        return error;
    }
    pFileStat->lastModified = lastModifiedNanos(&attr.ftLastWriteTime);
    if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        pFileStat->size = 0;
        pFileStat->fileType = FILE_TYPE_DIRECTORY;
    } else {
        pFileStat->size = ((LONG64) attr.nFileSizeHigh << 32) | attr.nFileSizeLow;
        pFileStat->fileType = FILE_TYPE_FILE;
    }
    return ERROR_SUCCESS;
#else    //WINDOWS_MIN: Windows Vista+ support for symlinks
    DWORD dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS;
    if (!followLink) {
        dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
    }
    HANDLE fileHandle = CreateFileW(
        pathStr,                                                   // lpFileName
        GENERIC_READ,                                              // dwDesiredAccess
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,    // dwShareMode
        NULL,                                                      // lpSecurityAttributes
        OPEN_EXISTING,                                             // dwCreationDisposition
        dwFlagsAndAttributes,                                      // dwFlagsAndAttributes
        NULL                                                       // hTemplateFile
    );
    if (fileHandle == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();
        if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND || error == ERROR_NOT_READY) {
            // Treat device with no media as missing
            pFileStat->lastModified = 0;
            pFileStat->size = 0;
            pFileStat->fileType = FILE_TYPE_MISSING;
            return ERROR_SUCCESS;
        }
        return error;
    }

    // This call allows retrieving almost everything except for the reparseTag
    BY_HANDLE_FILE_INFORMATION fileInfo;
    BOOL ok = GetFileInformationByHandle(fileHandle, &fileInfo);
    if (!ok) {
        DWORD error = GetLastError();
        CloseHandle(fileHandle);
        return error;
    }

    // This call allows retrieving the reparse tag
    FILE_ATTRIBUTE_TAG_INFO fileTagInfo;
    ok = GetFileInformationByHandleEx(fileHandle, FileAttributeTagInfo, &fileTagInfo, sizeof(fileTagInfo));
    CloseHandle(fileHandle);

    if (!ok) {
        DWORD error = GetLastError();
        if (error == ERROR_INVALID_PARAMETER) {
            // It appears calling GetFileInformationByHandleEx with FILE_ATTRIBUTE_TAG_INFO
            // fails on FAT file system with ERROR_INVALID_PARAMETER.
            // Use the attributes from fileInfo instead to fill pFileStat.
            fillFileStat(pFileStat, false, fileInfo.dwFileAttributes, fileInfo);
            return ERROR_SUCCESS;
        } else {
            return error;
        }
    }

    fillFileStat(
        pFileStat,
        is_file_symlink(fileTagInfo.FileAttributes, fileTagInfo.ReparseTag),
        fileTagInfo.FileAttributes,
        fileInfo
    );
    return ERROR_SUCCESS;
#endif
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv* env, jclass target, jobject info, jobject result) {
    jclass infoClass = env->GetObjectClass(info);

    OSVERSIONINFOEX versionInfo;
    versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    if (GetVersionEx((OSVERSIONINFO*) &versionInfo) == 0) {
        mark_failed_with_errno(env, "could not get version info", result);
        return;
    }

    SYSTEM_INFO systemInfo;
    GetNativeSystemInfo(&systemInfo);
    jstring arch = NULL;
    if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
{'fraction_non_alphanumeric': 0.082733566385633, 'fraction_numerical': 0.005483583521831517, 'mean_word_length': 2.9722297849169617, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 49, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5508741', 'n_tokens_mistral': 4423, 'n_tokens_neox': 4278, 'n_words': 1013}