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 
11 #include <algorithm>
12 #include <climits>
13 #include <cstring>
14 #include <sstream>
15 
16 #include "flutter/fml/build_config.h"
17 #include "flutter/fml/mapping.h"
18 #include "flutter/fml/platform/win/errors_win.h"
19 #include "flutter/fml/platform/win/wstring_conversion.h"
20 
21 namespace fml {
22 
23 static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
24  wchar_t buffer[MAX_PATH] = {0};
25  const DWORD buffer_size = ::GetFinalPathNameByHandle(
26  handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED);
27  if (buffer_size == 0) {
28  FML_DLOG(ERROR) << "Could not get file handle path. "
30  return {};
31  }
32  return WideStringToString({buffer, buffer_size});
33 }
34 
35 static std::string GetAbsolutePath(const fml::UniqueFD& base_directory,
36  const char* subpath) {
37  std::stringstream stream;
38  stream << GetFullHandlePath(base_directory) << "\\" << subpath;
39  auto path = stream.str();
40  std::replace(path.begin(), path.end(), '/', '\\');
41  return path;
42 }
43 
44 static std::wstring GetTemporaryDirectoryPath() {
45  wchar_t wchar_path[MAX_PATH];
46  auto result_size = ::GetTempPath(MAX_PATH, wchar_path);
47  if (result_size > 0) {
48  return {wchar_path, result_size};
49  }
50  FML_DLOG(ERROR) << "Could not get temporary directory path. "
52  return {};
53 }
54 
55 static DWORD GetDesiredAccessFlags(FilePermission permission) {
56  switch (permission) {
58  return GENERIC_READ;
60  return GENERIC_WRITE;
62  return GENERIC_READ | GENERIC_WRITE;
63  }
64  return GENERIC_READ;
65 }
66 
67 static DWORD GetShareFlags(FilePermission permission) {
68  switch (permission) {
70  return FILE_SHARE_READ;
72  return FILE_SHARE_WRITE;
74  return FILE_SHARE_READ | FILE_SHARE_WRITE;
75  }
76  return FILE_SHARE_READ;
77 }
78 
79 static DWORD GetFileAttributesForUtf8Path(const char* absolute_path) {
80  return ::GetFileAttributes(StringToWideString(absolute_path).c_str());
81 }
82 
83 static DWORD GetFileAttributesForUtf8Path(const fml::UniqueFD& base_directory,
84  const char* path) {
85  std::string full_path = GetFullHandlePath(base_directory) + "\\" + path;
86  return GetFileAttributesForUtf8Path(full_path.c_str());
87 }
88 
89 std::string CreateTemporaryDirectory() {
90  // Get the system temporary directory.
91  auto temp_dir_container = GetTemporaryDirectoryPath();
92  if (temp_dir_container.size() == 0) {
93  FML_DLOG(ERROR) << "Could not get system temporary directory.";
94  return {};
95  }
96 
97  // Create a UUID.
98  UUID uuid;
99  RPC_STATUS status = UuidCreateSequential(&uuid);
100  if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
101  FML_DLOG(ERROR) << "Could not create UUID";
102  return {};
103  }
104 
105  RPC_WSTR uuid_string;
106  status = UuidToString(&uuid, &uuid_string);
107  if (status != RPC_S_OK) {
108  FML_DLOG(ERROR) << "Could not create UUID to string.";
109  return {};
110  }
111 
112  std::wstring uuid_str(reinterpret_cast<wchar_t*>(uuid_string));
113  RpcStringFree(&uuid_string);
114 
115  // Join the two and create a path to the new temporary directory.
116 
117  std::wstringstream stream;
118  stream << temp_dir_container << "\\" << uuid_str;
119  auto temp_dir = stream.str();
120 
121  auto dir_fd = OpenDirectory(WideStringToString(temp_dir).c_str(), true,
123  if (!dir_fd.is_valid()) {
124  FML_DLOG(ERROR) << "Could not get temporary directory FD. "
125  << GetLastErrorMessage();
126  return {};
127  }
128 
129  return WideStringToString(std::move(temp_dir));
130 }
131 
132 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
133  const char* path,
134  bool create_if_necessary,
135  FilePermission permission) {
136  return OpenFile(GetAbsolutePath(base_directory, path).c_str(),
137  create_if_necessary, permission);
138 }
139 
140 fml::UniqueFD OpenFile(const char* path,
141  bool create_if_necessary,
142  FilePermission permission) {
143  if (path == nullptr || strlen(path) == 0) {
144  return {};
145  }
146 
147  auto file_name = StringToWideString({path});
148 
149  if (file_name.size() == 0) {
150  return {};
151  }
152 
153  const DWORD creation_disposition =
154  create_if_necessary ? CREATE_NEW : OPEN_EXISTING;
155 
156  const DWORD flags = FILE_ATTRIBUTE_NORMAL;
157 
158  auto handle =
159  CreateFile(file_name.c_str(), // lpFileName
160  GetDesiredAccessFlags(permission), // dwDesiredAccess
161  GetShareFlags(permission), // dwShareMode
162  nullptr, // lpSecurityAttributes //
163  creation_disposition, // dwCreationDisposition //
164  flags, // dwFlagsAndAttributes //
165  nullptr // hTemplateFile //
166  );
167 
168  if (handle == INVALID_HANDLE_VALUE) {
169  FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
170  return {};
171  }
172 
173  return fml::UniqueFD{handle};
174 }
175 
176 fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
177  const char* path,
178  bool create_if_necessary,
179  FilePermission permission) {
180  return OpenDirectory(GetAbsolutePath(base_directory, path).c_str(),
181  create_if_necessary, permission);
182 }
183 
184 fml::UniqueFD OpenDirectory(const char* path,
185  bool create_if_necessary,
186  FilePermission permission) {
187  if (path == nullptr || strlen(path) == 0) {
188  return {};
189  }
190 
191  auto file_name = StringToWideString({path});
192 
193  if (file_name.size() == 0) {
194  return {};
195  }
196 
197  if (create_if_necessary) {
198  if (!::CreateDirectory(file_name.c_str(), nullptr)) {
199  if (GetLastError() != ERROR_ALREADY_EXISTS) {
200  FML_DLOG(ERROR) << "Could not create directory. "
201  << GetLastErrorMessage();
202  return {};
203  }
204  }
205  }
206 
207  const DWORD creation_disposition = OPEN_EXISTING;
208 
209  const DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
210 
211  auto handle =
212  CreateFile(file_name.c_str(), // lpFileName
213  GetDesiredAccessFlags(permission), // dwDesiredAccess
214  GetShareFlags(permission), // dwShareMode
215  nullptr, // lpSecurityAttributes //
216  creation_disposition, // dwCreationDisposition //
217  flags, // dwFlagsAndAttributes //
218  nullptr // hTemplateFile //
219  );
220 
221  if (handle == INVALID_HANDLE_VALUE) {
222  FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
223  return {};
224  }
225 
226  return fml::UniqueFD{handle};
227 }
228 
230  if (descriptor == INVALID_HANDLE_VALUE) {
231  return fml::UniqueFD{};
232  }
233 
234  HANDLE duplicated = INVALID_HANDLE_VALUE;
235 
236  if (!::DuplicateHandle(
237  GetCurrentProcess(), // source process
238  descriptor, // source handle
239  GetCurrentProcess(), // target process
240  &duplicated, // target handle
241  0, // desired access (ignored because DUPLICATE_SAME_ACCESS)
242  FALSE, // inheritable
243  DUPLICATE_SAME_ACCESS) // options
244  ) {
245  return fml::UniqueFD{};
246  }
247 
248  return fml::UniqueFD{duplicated};
249 }
250 
251 bool IsDirectory(const fml::UniqueFD& directory) {
252  BY_HANDLE_FILE_INFORMATION info;
253  if (!::GetFileInformationByHandle(directory.get(), &info)) {
254  FML_DLOG(ERROR) << "Could not get file information. "
255  << GetLastErrorMessage();
256  return false;
257  }
258  return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
259 }
260 
261 bool IsDirectory(const fml::UniqueFD& base_directory, const char* path) {
262  return GetFileAttributesForUtf8Path(base_directory, path) &
263  FILE_ATTRIBUTE_DIRECTORY;
264 }
265 
266 bool IsFile(const std::string& path) {
267  DWORD attributes = GetFileAttributesForUtf8Path(path.c_str());
268  if (attributes == INVALID_FILE_ATTRIBUTES) {
269  return false;
270  }
271  return !(attributes &
272  (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
273 }
274 
275 bool UnlinkDirectory(const char* path) {
276  if (!::RemoveDirectory(StringToWideString(path).c_str())) {
277  FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
278  << GetLastErrorMessage();
279  return false;
280  }
281  return true;
282 }
283 
284 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
285  if (!::RemoveDirectory(
286  StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
287  FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
288  << GetLastErrorMessage();
289  return false;
290  }
291  return true;
292 }
293 
294 bool UnlinkFile(const char* path) {
295  if (!::DeleteFile(StringToWideString(path).c_str())) {
296  FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
297  << GetLastErrorMessage();
298  return false;
299  }
300  return true;
301 }
302 
303 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
304  if (!::DeleteFile(
305  StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
306  FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
307  << GetLastErrorMessage();
308  return false;
309  }
310  return true;
311 }
312 
313 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
314  LARGE_INTEGER large_size;
315  large_size.QuadPart = size;
316  large_size.LowPart = SetFilePointer(file.get(), large_size.LowPart,
317  &large_size.HighPart, FILE_BEGIN);
318  if (large_size.LowPart == INVALID_SET_FILE_POINTER &&
319  GetLastError() != NO_ERROR) {
320  FML_DLOG(ERROR) << "Could not update file size. " << GetLastErrorMessage();
321  return false;
322  }
323 
324  if (!::SetEndOfFile(file.get())) {
325  FML_DLOG(ERROR) << "Could not commit file size update. "
326  << GetLastErrorMessage();
327  return false;
328  }
329  return true;
330 }
331 
332 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
333  return GetFileAttributesForUtf8Path(base_directory, path) !=
334  INVALID_FILE_ATTRIBUTES;
335 }
336 
337 bool WriteAtomically(const fml::UniqueFD& base_directory,
338  const char* file_name,
339  const Mapping& mapping) {
340  if (file_name == nullptr) {
341  return false;
342  }
343 
344  auto file_path = GetAbsolutePath(base_directory, file_name);
345  std::stringstream stream;
346  stream << file_path << ".temp";
347  auto temp_file_path = stream.str();
348 
349  auto temp_file =
350  OpenFile(temp_file_path.c_str(), true, FilePermission::kReadWrite);
351 
352  if (!temp_file.is_valid()) {
353  FML_DLOG(ERROR) << "Could not create temporary file.";
354  return false;
355  }
356 
357  if (!TruncateFile(temp_file, mapping.GetSize())) {
358  FML_DLOG(ERROR) << "Could not truncate the file to the correct size. "
359  << GetLastErrorMessage();
360  return false;
361  }
362 
363  {
364  FileMapping temp_file_mapping(temp_file, {FileMapping::Protection::kRead,
366  if (temp_file_mapping.GetSize() != mapping.GetSize()) {
367  FML_DLOG(ERROR) << "Temporary file mapping size was incorrect. Is "
368  << temp_file_mapping.GetSize() << ". Should be "
369  << mapping.GetSize() << ".";
370  return false;
371  }
372 
373  if (temp_file_mapping.GetMutableMapping() == nullptr) {
374  FML_DLOG(ERROR) << "Temporary file does not have a mutable mapping.";
375  return false;
376  }
377 
378  ::memcpy(temp_file_mapping.GetMutableMapping(), mapping.GetMapping(),
379  mapping.GetSize());
380 
381  if (!::FlushViewOfFile(temp_file_mapping.GetMutableMapping(),
382  mapping.GetSize())) {
383  FML_DLOG(ERROR) << "Could not flush file view. " << GetLastErrorMessage();
384  return false;
385  }
386 
387  if (!::FlushFileBuffers(temp_file.get())) {
388  FML_DLOG(ERROR) << "Could not flush file buffers. "
389  << GetLastErrorMessage();
390  return false;
391  }
392 
393  // File mapping is detroyed.
394  }
395 
396  temp_file.reset();
397 
398  if (!::MoveFile(StringToWideString(temp_file_path).c_str(),
399  StringToWideString(file_path).c_str())) {
400  FML_DLOG(ERROR)
401  << "Could not replace temp file at correct path. File path: "
402  << file_path << ". Temp file path: " << temp_file_path << " "
403  << GetLastErrorMessage();
404  return false;
405  }
406 
407  return true;
408 }
409 
410 bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) {
411  std::string search_pattern = GetFullHandlePath(directory) + "\\*";
412  WIN32_FIND_DATA find_file_data;
413  HANDLE find_handle = ::FindFirstFile(
414  StringToWideString(search_pattern).c_str(), &find_file_data);
415 
416  if (find_handle == INVALID_HANDLE_VALUE) {
417  FML_DLOG(ERROR) << "Can't open the directory. Error: "
418  << GetLastErrorMessage();
419  return true; // continue to visit other files
420  }
421 
422  do {
423  std::string filename = WideStringToString(find_file_data.cFileName);
424  if (filename != "." && filename != "..") {
425  if (!visitor(directory, filename)) {
426  ::FindClose(find_handle);
427  return false;
428  }
429  }
430  } while (::FindNextFile(find_handle, &find_file_data));
431  ::FindClose(find_handle);
432  return true;
433 }
434 
435 } // namespace fml
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
static DWORD GetDesiredAccessFlags(FilePermission permission)
Definition: file_win.cc:55
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:23
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:79
static std::string GetFullHandlePath(const fml::UniqueFD &handle)
Definition: file_win.cc:23
virtual const uint8_t * GetMapping() const =0
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor)
Definition: file_posix.cc:120
static std::wstring GetTemporaryDirectoryPath()
Definition: file_win.cc:44
const T & get() const
Definition: unique_object.h:87
Definition: ascii_trie.cc:9
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:94
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:233
FilePermission
Definition: file.h:24
bool IsDirectory(const fml::UniqueFD &directory)
Definition: file_posix.cc:124
static DWORD GetShareFlags(FilePermission permission)
Definition: file_win.cc:67
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:180
bool UnlinkFile(const char *path)
Definition: file_posix.cc:168
bool IsFile(const std::string &path)
Definition: file_posix.cc:143
std::wstring StringToWideString(const std::string &str)
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:64
static std::string GetAbsolutePath(const fml::UniqueFD &base_directory, const char *subpath)
Definition: file_win.cc:35
#define FML_DLOG(severity)
Definition: logging.h:85
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:152
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:188
std::string GetLastErrorMessage()
Definition: errors_win.cc:15
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:160
std::string WideStringToString(const std::wstring &wstr)
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol Prefix for the symbols representing ICU data linked into the Flutter library dart flags
Definition: switches.h:66