Flutter Engine
file_win.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/fml/file.h"
6 
7 #include <Fileapi.h>
8 #include <Shlwapi.h>
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <sys/stat.h>
12 #include <string>
13 
14 #include <algorithm>
15 #include <climits>
16 #include <cstring>
17 #include <optional>
18 #include <sstream>
19 
20 #include "flutter/fml/build_config.h"
21 #include "flutter/fml/mapping.h"
22 #include "flutter/fml/platform/win/errors_win.h"
23 #include "flutter/fml/platform/win/wstring_conversion.h"
24 
25 namespace fml {
26 
27 static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
28  // Although the documentation claims that GetFinalPathNameByHandle is
29  // supported for UWP apps, turns out it returns ACCESS_DENIED in this case
30  // hence the need to workaround it by maintaining a map of file handles to
31  // absolute paths populated by fml::OpenDirectory.
32 #ifdef WINUWP
33  std::optional<fml::internal::os_win::DirCacheEntry> found =
34  fml::internal::os_win::UniqueFDTraits::GetCacheEntry(handle.get());
35 
36  if (found) {
37  FILE_ID_INFO info;
38 
39  BOOL result = GetFileInformationByHandleEx(
40  handle.get(), FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info,
41  sizeof(FILE_ID_INFO));
42 
43  // Assuming it was possible to retrieve fileinfo, compare the id field. The
44  // handle hasn't been reused if the file identifier is the same as when it
45  // was cached
46  if (result && memcmp(found.value().id.Identifier, info.FileId.Identifier,
47  sizeof(FILE_ID_INFO))) {
48  return WideStringToString(found.value().filename);
49  } else {
50  fml::internal::os_win::UniqueFDTraits::RemoveCacheEntry(handle.get());
51  }
52  }
53 
54  return std::string();
55 #else
56  wchar_t buffer[MAX_PATH] = {0};
57  const DWORD buffer_size = ::GetFinalPathNameByHandle(
58  handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED);
59  if (buffer_size == 0) {
60  FML_DLOG(ERROR) << "Could not get file handle path. "
62  return {};
63  }
64  return WideStringToString({buffer, buffer_size});
65 #endif
66 }
67 
68 static std::string GetAbsolutePath(const fml::UniqueFD& base_directory,
69  const char* subpath) {
70  std::stringstream stream;
71  stream << GetFullHandlePath(base_directory) << "\\" << subpath;
72  auto path = stream.str();
73  std::replace(path.begin(), path.end(), '/', '\\');
74  return path;
75 }
76 
77 static std::wstring GetTemporaryDirectoryPath() {
78  wchar_t wchar_path[MAX_PATH];
79  auto result_size = ::GetTempPath(MAX_PATH, wchar_path);
80  if (result_size > 0) {
81  return {wchar_path, result_size};
82  }
83  FML_DLOG(ERROR) << "Could not get temporary directory path. "
85  return {};
86 }
87 
89  switch (permission) {
91  return GENERIC_READ;
93  return GENERIC_WRITE;
95  return GENERIC_READ | GENERIC_WRITE;
96  }
97  return GENERIC_READ;
98 }
99 
100 static DWORD GetShareFlags(FilePermission permission) {
101  switch (permission) {
103  return FILE_SHARE_READ;
105  return FILE_SHARE_WRITE;
107  return FILE_SHARE_READ | FILE_SHARE_WRITE;
108  }
109  return FILE_SHARE_READ;
110 }
111 
112 static DWORD GetFileAttributesForUtf8Path(const char* absolute_path) {
113  return ::GetFileAttributes(StringToWideString(absolute_path).c_str());
114 }
115 
116 static DWORD GetFileAttributesForUtf8Path(const fml::UniqueFD& base_directory,
117  const char* path) {
118  std::string full_path = GetFullHandlePath(base_directory) + "\\" + path;
119  return GetFileAttributesForUtf8Path(full_path.c_str());
120 }
121 
122 std::string CreateTemporaryDirectory() {
123  // Get the system temporary directory.
124  auto temp_dir_container = GetTemporaryDirectoryPath();
125  if (temp_dir_container.size() == 0) {
126  FML_DLOG(ERROR) << "Could not get system temporary directory.";
127  return {};
128  }
129 
130  // Create a UUID.
131  UUID uuid;
132  RPC_STATUS status = UuidCreateSequential(&uuid);
133  if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
134  FML_DLOG(ERROR) << "Could not create UUID";
135  return {};
136  }
137 
138  RPC_WSTR uuid_string;
139  status = UuidToString(&uuid, &uuid_string);
140  if (status != RPC_S_OK) {
141  FML_DLOG(ERROR) << "Could not create UUID to string.";
142  return {};
143  }
144 
145  std::wstring uuid_str(reinterpret_cast<wchar_t*>(uuid_string));
146  RpcStringFree(&uuid_string);
147 
148  // Join the two and create a path to the new temporary directory.
149 
150  std::wstringstream stream;
151  stream << temp_dir_container << "\\" << uuid_str;
152  auto temp_dir = stream.str();
153 
154  auto dir_fd = OpenDirectory(WideStringToString(temp_dir).c_str(), true,
156  if (!dir_fd.is_valid()) {
157  FML_DLOG(ERROR) << "Could not get temporary directory FD. "
158  << GetLastErrorMessage();
159  return {};
160  }
161 
162  return WideStringToString(std::move(temp_dir));
163 }
164 
165 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
166  const char* path,
167  bool create_if_necessary,
168  FilePermission permission) {
169  return OpenFile(GetAbsolutePath(base_directory, path).c_str(),
170  create_if_necessary, permission);
171 }
172 
173 fml::UniqueFD OpenFile(const char* path,
174  bool create_if_necessary,
175  FilePermission permission) {
176  if (path == nullptr || strlen(path) == 0) {
177  return {};
178  }
179 
180  auto file_name = StringToWideString({path});
181 
182  if (file_name.size() == 0) {
183  return {};
184  }
185 
186  const DWORD creation_disposition =
187  create_if_necessary ? CREATE_NEW : OPEN_EXISTING;
188 
189  const DWORD flags = FILE_ATTRIBUTE_NORMAL;
190 
191  auto handle =
192  CreateFile(file_name.c_str(), // lpFileName
193  GetDesiredAccessFlags(permission), // dwDesiredAccess
194  GetShareFlags(permission), // dwShareMode
195  nullptr, // lpSecurityAttributes //
196  creation_disposition, // dwCreationDisposition //
197  flags, // dwFlagsAndAttributes //
198  nullptr // hTemplateFile //
199  );
200 
201  if (handle == INVALID_HANDLE_VALUE) {
202  FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
203  return {};
204  }
205 
206  return fml::UniqueFD{handle};
207 }
208 
209 fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
210  const char* path,
211  bool create_if_necessary,
212  FilePermission permission) {
213  return OpenDirectory(GetAbsolutePath(base_directory, path).c_str(),
214  create_if_necessary, permission);
215 }
216 
217 fml::UniqueFD OpenDirectory(const char* path,
218  bool create_if_necessary,
219  FilePermission permission) {
220  if (path == nullptr || strlen(path) == 0) {
221  return {};
222  }
223 
224  auto file_name = StringToWideString({path});
225 
226  if (file_name.size() == 0) {
227  return {};
228  }
229 
230  if (create_if_necessary) {
231  if (!::CreateDirectory(file_name.c_str(), nullptr)) {
232  if (GetLastError() != ERROR_ALREADY_EXISTS) {
233  FML_DLOG(ERROR) << "Could not create directory. "
234  << GetLastErrorMessage();
235  return {};
236  }
237  }
238  }
239 
240  const DWORD creation_disposition = OPEN_EXISTING;
241 
242  const DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
243 
244  auto handle =
245  CreateFile(file_name.c_str(), // lpFileName
246  GetDesiredAccessFlags(permission), // dwDesiredAccess
247  GetShareFlags(permission), // dwShareMode
248  nullptr, // lpSecurityAttributes //
249  creation_disposition, // dwCreationDisposition //
250  flags, // dwFlagsAndAttributes //
251  nullptr // hTemplateFile //
252  );
253 
254  if (handle == INVALID_HANDLE_VALUE) {
255  FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
256  return {};
257  }
258 
259 #ifdef WINUWP
260  FILE_ID_INFO info;
261 
262  BOOL result = GetFileInformationByHandleEx(
263  handle, FILE_INFO_BY_HANDLE_CLASS::FileIdInfo, &info,
264  sizeof(FILE_ID_INFO));
265 
266  // Only cache if it is possible to get valid a fileinformation to extract the
267  // fileid to ensure correct handle versioning.
268  if (result) {
269  fml::internal::os_win::DirCacheEntry fc{file_name, info.FileId};
270 
271  fml::internal::os_win::UniqueFDTraits::StoreCacheEntry(handle, fc);
272  }
273 #endif
274 
275  return fml::UniqueFD{handle};
276 }
277 
279  if (descriptor == INVALID_HANDLE_VALUE) {
280  return fml::UniqueFD{};
281  }
282 
283  HANDLE duplicated = INVALID_HANDLE_VALUE;
284 
285  if (!::DuplicateHandle(
286  GetCurrentProcess(), // source process
287  descriptor, // source handle
288  GetCurrentProcess(), // target process
289  &duplicated, // target handle
290  0, // desired access (ignored because DUPLICATE_SAME_ACCESS)
291  FALSE, // inheritable
292  DUPLICATE_SAME_ACCESS) // options
293  ) {
294  return fml::UniqueFD{};
295  }
296 
297  return fml::UniqueFD{duplicated};
298 }
299 
300 bool IsDirectory(const fml::UniqueFD& directory) {
301  BY_HANDLE_FILE_INFORMATION info;
302  if (!::GetFileInformationByHandle(directory.get(), &info)) {
303  FML_DLOG(ERROR) << "Could not get file information. "
304  << GetLastErrorMessage();
305  return false;
306  }
307  return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
308 }
309 
310 bool IsDirectory(const fml::UniqueFD& base_directory, const char* path) {
311  return GetFileAttributesForUtf8Path(base_directory, path) &
312  FILE_ATTRIBUTE_DIRECTORY;
313 }
314 
315 bool IsFile(const std::string& path) {
316  DWORD attributes = GetFileAttributesForUtf8Path(path.c_str());
317  if (attributes == INVALID_FILE_ATTRIBUTES) {
318  return false;
319  }
320  return !(attributes &
321  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
322 }
323 
324 bool UnlinkDirectory(const char* path) {
325  if (!::RemoveDirectory(StringToWideString(path).c_str())) {
326  FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
327  << GetLastErrorMessage();
328  return false;
329  }
330  return true;
331 }
332 
333 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
334  if (!::RemoveDirectory(
335  StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
336  FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
337  << GetLastErrorMessage();
338  return false;
339  }
340  return true;
341 }
342 
343 bool UnlinkFile(const char* path) {
344  if (!::DeleteFile(StringToWideString(path).c_str())) {
345  FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
346  << GetLastErrorMessage();
347  return false;
348  }
349  return true;
350 }
351 
352 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
353  if (!::DeleteFile(
354  StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
355  FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
356  << GetLastErrorMessage();
357  return false;
358  }
359  return true;
360 }
361 
362 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
363  LARGE_INTEGER large_size;
364  large_size.QuadPart = size;
365  large_size.LowPart = SetFilePointer(file.get(), large_size.LowPart,
366  &large_size.HighPart, FILE_BEGIN);
367  if (large_size.LowPart == INVALID_SET_FILE_POINTER &&
368  GetLastError() != NO_ERROR) {
369  FML_DLOG(ERROR) << "Could not update file size. " << GetLastErrorMessage();
370  return false;
371  }
372 
373  if (!::SetEndOfFile(file.get())) {
374  FML_DLOG(ERROR) << "Could not commit file size update. "
375  << GetLastErrorMessage();
376  return false;
377  }
378  return true;
379 }
380 
381 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
382  return GetFileAttributesForUtf8Path(base_directory, path) !=
383  INVALID_FILE_ATTRIBUTES;
384 }
385 
386 bool WriteAtomically(const fml::UniqueFD& base_directory,
387  const char* file_name,
388  const Mapping& mapping) {
389  if (file_name == nullptr) {
390  return false;
391  }
392 
393  auto file_path = GetAbsolutePath(base_directory, file_name);
394  std::stringstream stream;
395  stream << file_path << ".temp";
396  auto temp_file_path = stream.str();
397 
398  auto temp_file =
399  OpenFile(temp_file_path.c_str(), true, FilePermission::kReadWrite);
400 
401  if (!temp_file.is_valid()) {
402  FML_DLOG(ERROR) << "Could not create temporary file.";
403  return false;
404  }
405 
406  if (!TruncateFile(temp_file, mapping.GetSize())) {
407  FML_DLOG(ERROR) << "Could not truncate the file to the correct size. "
408  << GetLastErrorMessage();
409  return false;
410  }
411 
412  {
413  FileMapping temp_file_mapping(temp_file, {FileMapping::Protection::kRead,
415  if (temp_file_mapping.GetSize() != mapping.GetSize()) {
416  FML_DLOG(ERROR) << "Temporary file mapping size was incorrect. Is "
417  << temp_file_mapping.GetSize() << ". Should be "
418  << mapping.GetSize() << ".";
419  return false;
420  }
421 
422  if (temp_file_mapping.GetMutableMapping() == nullptr) {
423  FML_DLOG(ERROR) << "Temporary file does not have a mutable mapping.";
424  return false;
425  }
426 
427  ::memcpy(temp_file_mapping.GetMutableMapping(), mapping.GetMapping(),
428  mapping.GetSize());
429 
430  if (!::FlushViewOfFile(temp_file_mapping.GetMutableMapping(),
431  mapping.GetSize())) {
432  FML_DLOG(ERROR) << "Could not flush file view. " << GetLastErrorMessage();
433  return false;
434  }
435 
436  if (!::FlushFileBuffers(temp_file.get())) {
437  FML_DLOG(ERROR) << "Could not flush file buffers. "
438  << GetLastErrorMessage();
439  return false;
440  }
441 
442  // File mapping is detroyed.
443  }
444 
445  temp_file.reset();
446 
447  if (!::MoveFile(StringToWideString(temp_file_path).c_str(),
448  StringToWideString(file_path).c_str())) {
449  FML_DLOG(ERROR)
450  << "Could not replace temp file at correct path. File path: "
451  << file_path << ". Temp file path: " << temp_file_path << " "
452  << GetLastErrorMessage();
453  return false;
454  }
455 
456  return true;
457 }
458 
459 bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) {
460  std::string search_pattern = GetFullHandlePath(directory) + "\\*";
461  WIN32_FIND_DATA find_file_data;
462  HANDLE find_handle = ::FindFirstFile(
463  StringToWideString(search_pattern).c_str(), &find_file_data);
464 
465  if (find_handle == INVALID_HANDLE_VALUE) {
466  FML_DLOG(ERROR) << "Can't open the directory. Error: "
467  << GetLastErrorMessage();
468  return true; // continue to visit other files
469  }
470 
471  do {
472  std::string filename = WideStringToString(find_file_data.cFileName);
473  if (filename != "." && filename != "..") {
474  if (!visitor(directory, filename)) {
475  ::FindClose(find_handle);
476  return false;
477  }
478  }
479  } while (::FindNextFile(find_handle, &find_file_data));
480  ::FindClose(find_handle);
481  return true;
482 }
483 
484 } // namespace fml
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
static DWORD GetDesiredAccessFlags(FilePermission permission)
Definition: file_win.cc:88
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:24
virtual size_t GetSize() const =0
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition: file.h:98
static DWORD GetFileAttributesForUtf8Path(const char *absolute_path)
Definition: file_win.cc:112
#define DeleteFile
static std::string GetFullHandlePath(const fml::UniqueFD &handle)
Definition: file_win.cc:27
GAsyncResult * result
virtual const uint8_t * GetMapping() const =0
#define GetFileAttributes
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor)
Definition: file_posix.cc:122
static std::wstring GetTemporaryDirectoryPath()
Definition: file_win.cc:77
const T & get() const
Definition: unique_object.h:87
FlutterSemanticsFlag flags
Definition: ascii_trie.cc:9
#define RemoveDirectory
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:96
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:235
FilePermission
Definition: file.h:24
bool IsDirectory(const fml::UniqueFD &directory)
Definition: file_posix.cc:126
static DWORD GetShareFlags(FilePermission permission)
Definition: file_win.cc:100
static fml::UniqueFD CreateDirectory(const fml::UniqueFD &base_directory, const std::vector< std::string > &components, FilePermission permission, size_t index)
Definition: file.cc:12
bool FileExists(const fml::UniqueFD &base_directory, const char *path)
Definition: file_posix.cc:182
#define INVALID_HANDLE_VALUE
unsigned long DWORD
Definition: windows_types.h:22
#define CreateFile
int BOOL
Definition: windows_types.h:37
void * HANDLE
Definition: windows_types.h:36
bool UnlinkFile(const char *path)
Definition: file_posix.cc:170
bool IsFile(const std::string &path)
Definition: file_posix.cc:145
std::wstring StringToWideString(const std::string &str)
#define FindNextFile
static const uint8_t buffer[]
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
fml::UniqueFD OpenFile(const char *path, bool create_if_necessary, FilePermission permission)
This can open a directory on POSIX, but not on Windows.
Definition: file_posix.cc:65
static std::string GetAbsolutePath(const fml::UniqueFD &base_directory, const char *subpath)
Definition: file_win.cc:68
#define FML_DLOG(severity)
Definition: logging.h:85
return FALSE
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:154
#define FindFirstFile
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:190
std::string GetLastErrorMessage()
Definition: errors_win.cc:15
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:162
#define MAX_PATH
std::string WideStringToString(const std::wstring &wstr)