Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions fml/platform/win/file_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
#include <Fileapi.h>
#include <Shlwapi.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <string>

#include <algorithm>
#include <climits>
#include <cstring>
#include <optional>
#include <sstream>

#include "flutter/fml/build_config.h"
Expand All @@ -21,6 +25,34 @@
namespace fml {

static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
// Although the documentation claims that GetFinalPathNameByHandle is
// supported for UWP apps, turns out it returns ACCESS_DENIED in this case
// hence the need to workaround it by maintaining a map of file handles to
// absolute paths populated by fml::OpenDirectory.
#ifdef WINUWP
std::optional<fml::internal::os_win::DirCacheEntry> found =
fml::internal::os_win::UniqueFDTraits::GetCacheEntry(handle.get());

if (found) {
FILE_ID_INFO info;

BOOL result = GetFileInformationByHandleEx(
handle.get(), FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info,
sizeof(FILE_ID_INFO));

// Assuming it was possible to retrieve fileinfo, compare the id field. The
// handle hasn't been reused if the file identifier is the same as when it
// was cached
if (result && memcmp(found.value().id.Identifier, info.FileId.Identifier,
sizeof(FILE_ID_INFO))) {
return WideStringToString(found.value().filename);
} else {
fml::internal::os_win::UniqueFDTraits::RemoveCacheEntry(handle.get());
}
}

return std::string();
#else
wchar_t buffer[MAX_PATH] = {0};
const DWORD buffer_size = ::GetFinalPathNameByHandle(
handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED);
Expand All @@ -30,6 +62,7 @@ static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
return {};
}
return WideStringToString({buffer, buffer_size});
#endif
}

static std::string GetAbsolutePath(const fml::UniqueFD& base_directory,
Expand Down Expand Up @@ -223,6 +256,22 @@ fml::UniqueFD OpenDirectory(const char* path,
return {};
}

#ifdef WINUWP
FILE_ID_INFO info;

BOOL result = GetFileInformationByHandleEx(
handle, FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info,
sizeof(FILE_ID_INFO));

// Only cache if it is possible to get valid a fileinformation to extract the
// fileid to ensure correct handle versioning.
if (result) {
fml::internal::os_win::DirCacheEntry fc{file_name, info.FileId};

fml::internal::os_win::UniqueFDTraits::StoreCacheEntry(handle, fc);
}
#endif

return fml::UniqueFD{handle};
}

Expand Down
5 changes: 4 additions & 1 deletion fml/unique_fd.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ namespace internal {

namespace os_win {

void UniqueFDTraits::Free(HANDLE fd) {
std::mutex UniqueFDTraits::file_map_mutex;
std::map<HANDLE, DirCacheEntry> UniqueFDTraits::file_map;

void UniqueFDTraits::Free_Handle(HANDLE fd) {
CloseHandle(fd);
}

Expand Down
41 changes: 40 additions & 1 deletion fml/unique_fd.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

#if OS_WIN
#include <windows.h>
#include <map>
#include <mutex>
#include <optional>
#else // OS_WIN
#include <dirent.h>
#include <unistd.h>
Expand All @@ -22,10 +25,46 @@ namespace internal {

namespace os_win {

struct DirCacheEntry {
std::wstring filename;
FILE_ID_128 id;
};

// The order of these is important. Must come before UniqueFDTraits struct
// else linker error. Embedding in struct also causes linker error.

struct UniqueFDTraits {
static std::mutex file_map_mutex;
static std::map<HANDLE, DirCacheEntry> file_map;

static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
static bool IsValid(HANDLE value) { return value != InvalidValue(); }
static void Free(HANDLE fd);
static void Free_Handle(HANDLE fd);

static void Free(HANDLE fd) {
RemoveCacheEntry(fd);

UniqueFDTraits::Free_Handle(fd);
}

static void RemoveCacheEntry(HANDLE fd) {
const std::lock_guard<std::mutex> lock(file_map_mutex);

file_map.erase(fd);
}

static void StoreCacheEntry(HANDLE fd, DirCacheEntry state) {
const std::lock_guard<std::mutex> lock(file_map_mutex);
file_map[fd] = state;
}

static std::optional<DirCacheEntry> GetCacheEntry(HANDLE fd) {
const std::lock_guard<std::mutex> lock(file_map_mutex);
auto found = file_map.find(fd);
return found == file_map.end()
? std::nullopt
: std::optional<DirCacheEntry>{found->second};
}
};

} // namespace os_win
Expand Down