Flutter Engine
The 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
25namespace fml {
26
27static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
28 wchar_t buffer[MAX_PATH] = {0};
29 const DWORD buffer_size = ::GetFinalPathNameByHandle(
30 handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED);
31 if (buffer_size == 0) {
32 return {};
33 }
35}
36
37static bool IsAbsolutePath(const char* path) {
38 if (path == nullptr || strlen(path) == 0) {
39 return false;
40 }
41
42 auto wpath = Utf8ToWideString({path});
43 if (wpath.empty()) {
44 return false;
45 }
46
47 return ::PathIsRelative(wpath.c_str()) == FALSE;
48}
49
50static std::string GetAbsolutePath(const fml::UniqueFD& base_directory,
51 const char* subpath) {
52 std::string path;
54 path = subpath;
55 } else {
56 std::stringstream stream;
57 stream << GetFullHandlePath(base_directory) << "\\" << subpath;
58 path = stream.str();
59 }
60 std::replace(path.begin(), path.end(), '/', '\\');
61 return path;
62}
63
64static std::wstring GetTemporaryDirectoryPath() {
65 wchar_t wchar_path[MAX_PATH];
66 auto result_size = ::GetTempPath(MAX_PATH, wchar_path);
67 if (result_size > 0) {
68 return {wchar_path, result_size};
69 }
70 return {};
71}
72
74 switch (permission) {
76 return GENERIC_READ;
78 return GENERIC_WRITE;
80 return GENERIC_READ | GENERIC_WRITE;
81 }
82 return GENERIC_READ;
83}
84
86 switch (permission) {
88 return FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
90 return 0;
92 return 0;
93 }
94 return FILE_SHARE_READ;
95}
96
97static DWORD GetFileAttributesForUtf8Path(const char* absolute_path) {
98 return ::GetFileAttributes(Utf8ToWideString(absolute_path).c_str());
99}
100
102 const char* path) {
103 std::string full_path = GetAbsolutePath(base_directory, path);
104 return GetFileAttributesForUtf8Path(full_path.c_str());
105}
106
107std::string CreateTemporaryDirectory() {
108 // Get the system temporary directory.
109 auto temp_dir_container = GetTemporaryDirectoryPath();
110 if (temp_dir_container.empty()) {
111 FML_DLOG(ERROR) << "Could not get system temporary directory.";
112 return {};
113 }
114
115 // Create a UUID.
116 UUID uuid;
117 RPC_STATUS status = UuidCreateSequential(&uuid);
118 if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
119 FML_DLOG(ERROR) << "Could not create UUID for temporary directory.";
120 return {};
121 }
122
123 RPC_WSTR uuid_string;
124 status = UuidToString(&uuid, &uuid_string);
125 if (status != RPC_S_OK) {
126 FML_DLOG(ERROR) << "Could not map UUID to string for temporary directory.";
127 return {};
128 }
129
130 std::wstring uuid_str(reinterpret_cast<wchar_t*>(uuid_string));
131 RpcStringFree(&uuid_string);
132
133 // Join the two and create a path to the new temporary directory.
134
135 std::wstringstream stream;
136 stream << temp_dir_container << "\\" << uuid_str;
137 auto temp_dir = stream.str();
138
139 auto dir_fd = OpenDirectory(WideStringToUtf8(temp_dir).c_str(), true,
141 if (!dir_fd.is_valid()) {
142 FML_DLOG(ERROR) << "Could not get temporary directory file descriptor. "
144 return {};
145 }
146
147 return WideStringToUtf8(std::move(temp_dir));
148}
149
150fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
151 const char* path,
152 bool create_if_necessary,
153 FilePermission permission) {
154 return OpenFile(GetAbsolutePath(base_directory, path).c_str(),
155 create_if_necessary, permission);
156}
157
158fml::UniqueFD OpenFile(const char* path,
159 bool create_if_necessary,
160 FilePermission permission) {
161 if (path == nullptr || strlen(path) == 0) {
162 return {};
163 }
164
165 auto file_name = Utf8ToWideString({path});
166
167 if (file_name.empty()) {
168 return {};
169 }
170
171 const DWORD creation_disposition =
172 create_if_necessary ? OPEN_ALWAYS : OPEN_EXISTING;
173
174 const DWORD flags = FILE_ATTRIBUTE_NORMAL;
175
176 auto handle =
177 CreateFile(file_name.c_str(), // lpFileName
178 GetDesiredAccessFlags(permission), // dwDesiredAccess
179 GetShareFlags(permission), // dwShareMode
180 nullptr, // lpSecurityAttributes //
181 creation_disposition, // dwCreationDisposition //
182 flags, // dwFlagsAndAttributes //
183 nullptr // hTemplateFile //
184 );
185
186 if (handle == INVALID_HANDLE_VALUE) {
187 return {};
188 }
189
190 return fml::UniqueFD{handle};
191}
192
193fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
194 const char* path,
195 bool create_if_necessary,
196 FilePermission permission) {
197 return OpenDirectory(GetAbsolutePath(base_directory, path).c_str(),
198 create_if_necessary, permission);
199}
200
202 bool create_if_necessary,
203 FilePermission permission) {
204 if (path == nullptr || strlen(path) == 0) {
205 return {};
206 }
207
208 auto file_name = Utf8ToWideString({path});
209
210 if (file_name.empty()) {
211 return {};
212 }
213
214 if (create_if_necessary) {
215 if (!::CreateDirectory(file_name.c_str(), nullptr)) {
216 if (GetLastError() != ERROR_ALREADY_EXISTS) {
217 FML_DLOG(ERROR) << "Could not create directory. "
219 return {};
220 }
221 }
222 }
223
224 const DWORD creation_disposition = OPEN_EXISTING;
225
226 const DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
227
228 auto handle =
229 CreateFile(file_name.c_str(), // lpFileName
230 GetDesiredAccessFlags(permission), // dwDesiredAccess
231 GetShareFlags(permission), // dwShareMode
232 nullptr, // lpSecurityAttributes //
233 creation_disposition, // dwCreationDisposition //
234 flags, // dwFlagsAndAttributes //
235 nullptr // hTemplateFile //
236 );
237
238 if (handle == INVALID_HANDLE_VALUE) {
239 return {};
240 }
241
242 return fml::UniqueFD{handle};
243}
244
246 if (descriptor == INVALID_HANDLE_VALUE) {
247 return fml::UniqueFD{};
248 }
249
250 HANDLE duplicated = INVALID_HANDLE_VALUE;
251
252 if (!::DuplicateHandle(
253 GetCurrentProcess(), // source process
254 descriptor, // source handle
255 GetCurrentProcess(), // target process
256 &duplicated, // target handle
257 0, // desired access (ignored because DUPLICATE_SAME_ACCESS)
258 FALSE, // inheritable
259 DUPLICATE_SAME_ACCESS) // options
260 ) {
261 return fml::UniqueFD{};
262 }
263
264 return fml::UniqueFD{duplicated};
265}
266
267bool IsDirectory(const fml::UniqueFD& directory) {
268 BY_HANDLE_FILE_INFORMATION info;
269 if (!::GetFileInformationByHandle(directory.get(), &info)) {
270 return false;
271 }
272 return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
273}
274
275bool IsDirectory(const fml::UniqueFD& base_directory, const char* path) {
276 return GetFileAttributesForUtf8Path(base_directory, path) &
277 FILE_ATTRIBUTE_DIRECTORY;
278}
279
280bool IsFile(const std::string& path) {
281 DWORD attributes = GetFileAttributesForUtf8Path(path.c_str());
282 if (attributes == INVALID_FILE_ATTRIBUTES) {
283 return false;
284 }
285 return !(attributes &
286 (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
287}
288
289bool UnlinkDirectory(const char* path) {
290 if (!::RemoveDirectory(Utf8ToWideString(path).c_str())) {
291 FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
293 return false;
294 }
295 return true;
296}
297
298bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
300 Utf8ToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
301 FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
303 return false;
304 }
305 return true;
306}
307
308bool UnlinkFile(const char* path) {
309 if (!::DeleteFile(Utf8ToWideString(path).c_str())) {
310 FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
312 return false;
313 }
314 return true;
315}
316
317bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
318 if (!::DeleteFile(
319 Utf8ToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
320 FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
322 return false;
323 }
324 return true;
325}
326
327bool TruncateFile(const fml::UniqueFD& file, size_t size) {
328 LARGE_INTEGER large_size;
329 large_size.QuadPart = size;
330 large_size.LowPart = SetFilePointer(file.get(), large_size.LowPart,
331 &large_size.HighPart, FILE_BEGIN);
332 if (large_size.LowPart == INVALID_SET_FILE_POINTER &&
333 GetLastError() != NO_ERROR) {
334 FML_DLOG(ERROR) << "Could not update file size. " << GetLastErrorMessage();
335 return false;
336 }
337
338 if (!::SetEndOfFile(file.get())) {
339 FML_DLOG(ERROR) << "Could not commit file size update. "
341 return false;
342 }
343 return true;
344}
345
346bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
347 return GetFileAttributesForUtf8Path(base_directory, path) !=
348 INVALID_FILE_ATTRIBUTES;
349}
350
351// TODO(jonahwilliams): https://github.com/flutter/flutter/issues/102838 this is
352// not atomic on Windows.
353bool WriteAtomically(const fml::UniqueFD& base_directory,
354 const char* file_name,
355 const Mapping& mapping) {
356 if (file_name == nullptr) {
357 return false;
358 }
359
360 auto file_path = GetAbsolutePath(base_directory, file_name);
361 auto temp_file =
362 OpenFile(file_path.c_str(), true, FilePermission::kReadWrite);
363
364 if (!temp_file.is_valid()) {
365 FML_DLOG(ERROR) << "Could not create file: " << file_path.c_str() << " "
366 << GetLastError() << " " << GetLastErrorMessage();
367 return false;
368 }
369
370 if (!TruncateFile(temp_file, mapping.GetSize())) {
371 FML_DLOG(ERROR) << "Could not truncate the file to the correct size. "
373 return false;
374 }
375
376 {
377 FileMapping temp_file_mapping(temp_file, {FileMapping::Protection::kRead,
379 if (temp_file_mapping.GetSize() != mapping.GetSize()) {
380 FML_DLOG(ERROR) << "Temporary file mapping size was incorrect. Is "
381 << temp_file_mapping.GetSize() << ". Should be "
382 << mapping.GetSize() << ".";
383 return false;
384 }
385
386 if (temp_file_mapping.GetMutableMapping() == nullptr) {
387 FML_DLOG(ERROR) << "Temporary file does not have a mutable mapping.";
388 return false;
389 }
390
391 ::memcpy(temp_file_mapping.GetMutableMapping(), mapping.GetMapping(),
392 mapping.GetSize());
393
394 if (!::FlushViewOfFile(temp_file_mapping.GetMutableMapping(),
395 mapping.GetSize())) {
396 FML_DLOG(ERROR) << "Could not flush file view. " << GetLastErrorMessage();
397 return false;
398 }
399
400 if (!::FlushFileBuffers(temp_file.get())) {
401 FML_DLOG(ERROR) << "Could not flush file buffers. "
403 return false;
404 }
405
406 // File mapping is detroyed.
407 }
408
409 temp_file.reset();
410
411 return true;
412}
413
414bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) {
415 std::string search_pattern = GetFullHandlePath(directory) + "\\*";
416 WIN32_FIND_DATA find_file_data;
417 HANDLE find_handle = ::FindFirstFile(Utf8ToWideString(search_pattern).c_str(),
418 &find_file_data);
419
420 if (find_handle == INVALID_HANDLE_VALUE) {
421 FML_DLOG(ERROR) << "Can't open the directory. Error: "
423 return true; // continue to visit other files
424 }
425
426 do {
427 std::string filename = WideStringToUtf8(find_file_data.cFileName);
428 if (filename != "." && filename != "..") {
429 if (!visitor(directory, filename)) {
430 ::FindClose(find_handle);
431 return false;
432 }
433 }
434 } while (::FindNextFile(find_handle, &find_file_data));
435 ::FindClose(find_handle);
436 return true;
437}
438
439} // namespace fml
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
static uint32_t buffer_size(uint32_t offset, uint32_t maxAlignment)
const T & get() const
Definition: unique_object.h:87
FlutterSemanticsFlag flags
#define FML_DLOG(severity)
Definition: logging.h:102
return FALSE
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
Definition: ascii_trie.cc:9
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:236
std::wstring Utf8ToWideString(const std::string_view str)
static std::string GetAbsolutePath(const fml::UniqueFD &base_directory, const char *subpath)
Definition: file_win.cc:50
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
static bool IsAbsolutePath(const char *path)
Definition: file_win.cc:37
static std::string GetFullHandlePath(const fml::UniqueFD &handle)
Definition: file_win.cc:27
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:191
fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor)
Definition: file_posix.cc:123
std::string GetLastErrorMessage()
Definition: errors_win.cc:15
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:97
bool IsFile(const std::string &path)
Definition: file_posix.cc:146
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:25
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:163
static DWORD GetFileAttributesForUtf8Path(const char *absolute_path)
Definition: file_win.cc:97
static std::wstring GetTemporaryDirectoryPath()
Definition: file_win.cc:64
static DWORD GetDesiredAccessFlags(FilePermission permission)
Definition: file_win.cc:73
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:155
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:183
std::string WideStringToUtf8(const std::wstring_view str)
bool UnlinkFile(const char *path)
Definition: file_posix.cc:171
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:66
static DWORD GetShareFlags(FilePermission permission)
Definition: file_win.cc:85
FilePermission
Definition: file.h:24
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition: file.h:98
bool IsDirectory(const fml::UniqueFD &directory)
Definition: file_posix.cc:127
def subpath(path, base_dir)
Definition: idlsync.py:142
#define ERROR(message)
Definition: elf_loader.cc:260
#define RemoveDirectory
#define GetFileAttributes
#define INVALID_HANDLE_VALUE
#define DeleteFile
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
#define FindNextFile
void * HANDLE
Definition: windows_types.h:36
#define MAX_PATH
#define FindFirstFile
unsigned long DWORD
Definition: windows_types.h:22
#define CreateFile