Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
file_win.cc
Go to the documentation of this file.
1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#if defined(DART_HOST_OS_WINDOWS)
7
8#include <functional>
9#include <memory>
10#include <utility>
11
12// clang-format off
13#include <Shlwapi.h> // NOLINT
14#include <fcntl.h> // NOLINT
15#include <io.h> // NOLINT
16#include <pathcch.h> // NOLINT
17#include <winioctl.h> // NOLINT
18#undef StrDup // defined in Shlwapi.h as StrDupW
19#include <stdio.h> // NOLINT
20#include <string.h> // NOLINT
21#include <sys/stat.h> // NOLINT
22#include <sys/utime.h> // NOLINT
23// clang-format on
24
25#include "bin/builtin.h"
26#include "bin/crypto.h"
27#include "bin/directory.h"
28#include "bin/file.h"
29#include "bin/file_win.h"
30#include "bin/namespace.h"
31#include "bin/utils.h"
32#include "bin/utils_win.h"
33#include "platform/syslog.h"
34#include "platform/utils.h"
35
36namespace dart {
37namespace bin {
38
39class FileHandle {
40 public:
41 explicit FileHandle(int fd) : fd_(fd) {}
42 ~FileHandle() {}
43 int fd() const { return fd_; }
44 void set_fd(int fd) { fd_ = fd; }
45
46 private:
47 int fd_;
48
49 DISALLOW_COPY_AND_ASSIGN(FileHandle);
50};
51
52File::~File() {
53 if (!IsClosed() && handle_->fd() != _fileno(stdout) &&
54 handle_->fd() != _fileno(stderr)) {
55 Close();
56 }
57 delete handle_;
58}
59
60void File::Close() {
61 ASSERT(handle_->fd() >= 0);
62 int closing_fd = handle_->fd();
63 if ((closing_fd == _fileno(stdout)) || (closing_fd == _fileno(stderr))) {
64 int fd = _open("NUL", _O_WRONLY);
65 ASSERT(fd >= 0);
66 _dup2(fd, closing_fd);
67 Utils::Close(fd);
68 } else {
69 int err = Utils::Close(closing_fd);
70 if (err != 0) {
71 Syslog::PrintErr("%s\n", strerror(errno));
72 }
73 }
74 handle_->set_fd(kClosedFd);
75}
76
77intptr_t File::GetFD() {
78 return handle_->fd();
79}
80
81bool File::IsClosed() {
82 return handle_->fd() == kClosedFd;
83}
84
85MappedMemory* File::Map(File::MapType type,
86 int64_t position,
87 int64_t length,
88 void* start) {
89 DWORD prot_alloc;
90 DWORD prot_final;
91 switch (type) {
92 case File::kReadOnly:
93 prot_alloc = PAGE_READWRITE;
94 prot_final = PAGE_READONLY;
95 break;
97 prot_alloc = PAGE_EXECUTE_READWRITE;
98 prot_final = PAGE_EXECUTE_READ;
99 break;
100 case File::kReadWrite:
101 prot_alloc = PAGE_READWRITE;
102 prot_final = PAGE_READWRITE;
103 break;
104 }
105
106 void* addr = start;
107 if (addr == nullptr) {
108 addr = VirtualAlloc(nullptr, length, MEM_COMMIT | MEM_RESERVE, prot_alloc);
109 if (addr == nullptr) {
110 Syslog::PrintErr("VirtualAlloc failed %d\n", GetLastError());
111 return nullptr;
112 }
113 }
114
115 const int64_t remaining_length = Length() - position;
116 SetPosition(position);
117 if (!ReadFully(addr, Utils::Minimum(length, remaining_length))) {
118 Syslog::PrintErr("ReadFully failed %d\n", GetLastError());
119 if (start == nullptr) {
120 VirtualFree(addr, 0, MEM_RELEASE);
121 }
122 return nullptr;
123 }
124
125 // If the requested mapping is larger than the file size, we should fill the
126 // extra memory with zeros.
127 if (length > remaining_length) {
128 memset(reinterpret_cast<uint8_t*>(addr) + remaining_length, 0,
129 length - remaining_length);
130 }
131
132 DWORD old_prot;
133 bool result = VirtualProtect(addr, length, prot_final, &old_prot);
134 if (!result) {
135 Syslog::PrintErr("VirtualProtect failed %d\n", GetLastError());
136 if (start == nullptr) {
137 VirtualFree(addr, 0, MEM_RELEASE);
138 }
139 return nullptr;
140 }
141 return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr);
142}
143
144void MappedMemory::Unmap() {
145 BOOL result = VirtualFree(address_, 0, MEM_RELEASE);
146 ASSERT(result);
147 address_ = nullptr;
148 size_ = 0;
149}
150
151int64_t File::Read(void* buffer, int64_t num_bytes) {
152 ASSERT(handle_->fd() >= 0);
153 return Utils::Read(handle_->fd(), buffer, num_bytes);
154}
155
156int64_t File::Write(const void* buffer, int64_t num_bytes) {
157 int fd = handle_->fd();
158 // Avoid narrowing conversion
159 ASSERT(fd >= 0 && num_bytes <= MAXDWORD && num_bytes >= 0);
160 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
161 DWORD written = 0;
162 BOOL result = WriteFile(handle, buffer, num_bytes, &written, nullptr);
163 if (!result) {
164 return -1;
165 }
166 DWORD mode;
167 int64_t bytes_written = written;
168 if (GetConsoleMode(handle, &mode)) {
169 // If `handle` is for a console, then `written` may refer to the number of
170 // characters printed to the screen rather than the number of bytes of the
171 // buffer that were actually consumed. To compute the number of bytes that
172 // were actually consumed, we convert the buffer to a wchar_t using the
173 // console's current code page, filling as many characters as were
174 // printed, and then convert that many characters back to the encoding for
175 // the code page, which gives the number of bytes of `buffer` used to
176 // generate the characters that were printed.
177 wchar_t* wide = new wchar_t[written];
178 int cp = GetConsoleOutputCP();
179 MultiByteToWideChar(cp, 0, reinterpret_cast<const char*>(buffer), -1, wide,
180 written);
181 int buffer_len =
182 WideCharToMultiByte(cp, 0, wide, written, nullptr, 0, nullptr, nullptr);
183 delete[] wide;
184 bytes_written = buffer_len;
185 }
186 return bytes_written;
187}
188
189bool File::VPrint(const char* format, va_list args) {
190 // Measure.
191 va_list measure_args;
192 va_copy(measure_args, args);
193 intptr_t len = _vscprintf(format, measure_args);
194 va_end(measure_args);
195
196 char* buffer = reinterpret_cast<char*>(malloc(len + 1));
197
198 // Print.
199 va_list print_args;
200 va_copy(print_args, args);
201 _vsnprintf(buffer, len + 1, format, print_args);
202 va_end(print_args);
203
204 bool result = WriteFully(buffer, len);
205 free(buffer);
206 return result;
207}
208
209int64_t File::Position() {
210 ASSERT(handle_->fd() >= 0);
211 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
212 LARGE_INTEGER zero_offset;
213 zero_offset.QuadPart = 0;
214 LARGE_INTEGER position;
215 if (!SetFilePointerEx(handle, zero_offset, &position, FILE_CURRENT)) {
216 return -1L;
217 }
218 return position.QuadPart;
219}
220
221bool File::SetPosition(int64_t position) {
222 ASSERT(handle_->fd() >= 0);
223 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
224 LARGE_INTEGER requested_position;
225 requested_position.QuadPart = position;
226 return SetFilePointerEx(handle, requested_position,
227 /*lpNewFilePointer=*/nullptr, FILE_BEGIN);
228}
229
230bool File::Truncate(int64_t length) {
231 if (!SetPosition(length)) {
232 return false;
233 }
234 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
235 return SetEndOfFile(handle);
236}
237
238bool File::Flush() {
239 ASSERT(handle_->fd());
240 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
241 return FlushFileBuffers(handle);
242}
243
244bool File::Lock(File::LockType lock, int64_t start, int64_t end) {
245 ASSERT(handle_->fd() >= 0);
246 ASSERT((end == -1) || (end > start));
247 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
248 OVERLAPPED overlapped;
249 ZeroMemory(&overlapped, sizeof(OVERLAPPED));
250
251 overlapped.Offset = Utils::Low32Bits(start);
252 overlapped.OffsetHigh = Utils::High32Bits(start);
253
254 int64_t length = end == -1 ? 0 : end - start;
255 if (length == 0) {
257 }
258 int32_t length_low = Utils::Low32Bits(length);
259 int32_t length_high = Utils::High32Bits(length);
260
261 BOOL rc;
262 switch (lock) {
264 rc = UnlockFileEx(handle, 0, length_low, length_high, &overlapped);
265 break;
270 DWORD flags = 0;
271 if ((lock == File::kLockShared) || (lock == File::kLockExclusive)) {
272 flags |= LOCKFILE_FAIL_IMMEDIATELY;
273 }
274 if ((lock == File::kLockExclusive) ||
276 flags |= LOCKFILE_EXCLUSIVE_LOCK;
277 }
278 rc = LockFileEx(handle, flags, 0, length_low, length_high, &overlapped);
279 break;
280 }
281 default:
282 UNREACHABLE();
283 }
284 return rc;
285}
286
287int64_t File::Length() {
288 ASSERT(handle_->fd() >= 0);
289 struct __stat64 st;
290 if (_fstat64(handle_->fd(), &st) == 0) {
291 return st.st_size;
292 }
293 return -1;
294}
295
296File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
297 int flags = O_RDONLY | O_BINARY | O_NOINHERIT;
298 if ((mode & kWrite) != 0) {
299 ASSERT((mode & kWriteOnly) == 0);
300 flags = (O_RDWR | O_CREAT | O_BINARY | O_NOINHERIT);
301 }
302 if ((mode & kWriteOnly) != 0) {
303 ASSERT((mode & kWrite) == 0);
304 flags = (O_WRONLY | O_CREAT | O_BINARY | O_NOINHERIT);
305 }
306 if ((mode & kTruncate) != 0) {
307 flags = flags | O_TRUNC;
308 }
309 int fd = _wopen(system_name, flags, 0666);
310 if (fd < 0) {
311 return nullptr;
312 }
313 if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
314 (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
315 int64_t position = _lseeki64(fd, 0, SEEK_END);
316 if (position < 0) {
317 return nullptr;
318 }
319 }
320 return OpenFD(fd);
321}
322
323File* File::OpenFD(int fd) {
324 return new File(new FileHandle(fd));
325}
326
327static std::unique_ptr<wchar_t[]> ConvertToAbsolutePath(
328 const std::unique_ptr<wchar_t[]>& path) {
329 // Initial buffer size is selected to avoid overallocating too much
330 // memory.
331 int buffer_size = 1024;
332 do {
333 auto buffer = std::make_unique<wchar_t[]>(buffer_size);
334 int full_path_length =
335 GetFullPathNameW(path.get(), buffer_size, buffer.get(),
336 /*lpFilePart=*/nullptr);
337 if (full_path_length == 0) {
338 return nullptr;
339 }
340
341 // Note: when sucessful full_path_length does *not* include terminating
342 // NUL character, but on failure it *does* include it when returning
343 // the size of buffer which we need. Hence comparison here is `<`, rather
344 // than `<=`.
345 if (full_path_length < buffer_size) {
346 return buffer;
347 }
348
349 buffer_size = full_path_length;
350 } while (true);
351}
352
353static bool IsAbsolutePath(const wchar_t* pathname) {
354 if (pathname == nullptr) return false;
355 char first = pathname[0];
356 char second = pathname[1];
357 if (first == L'\\' && second == L'\\') return true;
358 if (second != L':') return false;
359 first |= 0x20;
360 char third = pathname[2];
361 return (first >= L'a') && (first <= L'z') &&
362 (third == L'\\' || third == L'/');
363}
364
365// Converts the given UTF8 path to wide char. If resulting path does not
366// fit into MAX_PATH / MAX_DIRECTORY_PATH (or if |force_long_prefix| is true)
367// then converts the path to the absolute `\\?\`-prefixed form.
368//
369// Note:
370// 1. Some WinAPI functions (like SetCurrentDirectoryW) are always limited
371// to MAX_PATH long paths and converting to `\\?\`-prefixed form does not
372// remove this limitation. Always check Win API documentation.
373// 2. This function might change relative path to an absolute path.
374static std::unique_ptr<wchar_t[]> ToWinAPIPath(const char* utf8_path,
375 bool is_file,
376 bool force_long_prefix) {
377 auto path = Utf8ToWideChar(utf8_path);
378
379 // File name and Directory name have different size limit.
380 // Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation
381 const int path_short_limit = is_file ? MAX_PATH : MAX_DIRECTORY_PATH;
382
383 const wchar_t* kLongPathPrefix = L"\\\\?\\";
384 const int kLongPathPrefixLength = 4;
385
386 std::unique_ptr<wchar_t[]> absolute_path;
387 if (!IsAbsolutePath(path.get())) {
388 absolute_path = ConvertToAbsolutePath(path);
389 if (absolute_path == nullptr) {
390 return path;
391 }
392 } else {
393 absolute_path = std::move(path);
394 }
395
396 int path_length = wcslen(absolute_path.get());
397
398 if (!force_long_prefix && path_length < path_short_limit) {
399 if (path == nullptr) {
400 return absolute_path;
401 } else {
402 return path;
403 }
404 }
405
406 if (wcsncmp(absolute_path.get(), kLongPathPrefix, kLongPathPrefixLength) ==
407 0) {
408 return absolute_path;
409 }
410
411 // Add prefix and replace forward slashes with backward slashes.
412 auto result =
413 std::make_unique<wchar_t[]>(kLongPathPrefixLength + path_length + 1);
414 wcsncpy(result.get(), kLongPathPrefix, kLongPathPrefixLength);
415 for (int i = 0; i < path_length; i++) {
416 result.get()[kLongPathPrefixLength + i] =
417 absolute_path[i] == L'/' ? L'\\' : absolute_path[i];
418 }
419 result.get()[path_length + kLongPathPrefixLength] = L'\0';
420 return result;
421}
422
423// Converts the given UTF8 path to wide char. If resulting path does not
424// fit into MAX_DIRECTORY_PATH (or if |force_long_prefix| is true) then
425// converts the path to the absolute `\\?\`-prefixed form.
426//
427// Note:
428// 1. Some WinAPI functions (like SetCurrentDirectoryW) are always limited
429// to MAX_PATH long paths and converting to `\\?\`-prefixed form does not
430// remove this limitation. Always check Win API documentation.
431// 2. This function might change relative path to an absolute path.
432static std::unique_ptr<wchar_t[]> ToWinAPIFilePath(
433 const char* path,
434 bool force_long_prefix = false) {
435 return ToWinAPIPath(path, /*is_file=*/true, force_long_prefix);
436}
437
438std::unique_ptr<wchar_t[]> ToWinAPIDirectoryPath(
439 const char* path,
440 bool force_long_prefix /* = false */) {
441 return ToWinAPIPath(path, /*is_file=*/false, force_long_prefix);
442}
443
444File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
445 const auto path = ToWinAPIFilePath(name);
446 File* file = FileOpenW(path.get(), mode);
447 return file;
448}
449
451 UriDecoder uri_decoder(uri);
452 if (uri_decoder.decoded() == nullptr) {
453 SetLastError(ERROR_INVALID_NAME);
454 return Utils::CreateCStringUniquePtr(nullptr);
455 }
456
457 const auto uri_w = Utf8ToWideChar(uri_decoder.decoded());
458 if (!UrlIsFileUrlW(uri_w.get())) {
459 return Utils::CreateCStringUniquePtr(Utils::StrDup(uri_decoder.decoded()));
460 }
461 wchar_t filename_w[MAX_PATH];
462 DWORD filename_len = MAX_PATH;
463 HRESULT result = PathCreateFromUrlW(uri_w.get(), filename_w, &filename_len,
464 /* dwFlags= */ 0);
465 if (result != S_OK) {
466 return Utils::CreateCStringUniquePtr(nullptr);
467 }
468
469 WideToUtf8Scope utf8_path(filename_w);
470 return utf8_path.release();
471}
472
473File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) {
474 auto path = UriToPath(uri);
475 if (path == nullptr) {
476 return nullptr;
477 }
478 return Open(namespc, path.get(), mode);
479}
480
481File* File::OpenStdio(int fd) {
482 int stdio_fd = -1;
483 switch (fd) {
484 case 1:
485 stdio_fd = _fileno(stdout);
486 break;
487 case 2:
488 stdio_fd = _fileno(stderr);
489 break;
490 default:
491 UNREACHABLE();
492 }
493 _setmode(stdio_fd, _O_BINARY);
494 return new File(new FileHandle(stdio_fd));
495}
496
497static bool StatHelper(const wchar_t* path, struct __stat64* st) {
498 int stat_status = _wstat64(path, st);
499 if (stat_status != 0) {
500 return false;
501 }
502 if ((st->st_mode & S_IFMT) != S_IFREG) {
503 SetLastError(ERROR_NOT_SUPPORTED);
504 return false;
505 }
506 return true;
507}
508
509static bool FileExists(const wchar_t* path) {
510 struct __stat64 st;
511 return StatHelper(path, &st);
512}
513
514bool File::Exists(Namespace* namespc, const char* name) {
515 const auto path = ToWinAPIFilePath(name);
516 return FileExists(path.get());
517}
518
519bool File::ExistsUri(Namespace* namespc, const char* uri) {
520 UriDecoder uri_decoder(uri);
521 if (uri_decoder.decoded() == nullptr) {
522 SetLastError(ERROR_INVALID_NAME);
523 return false;
524 }
525 return File::Exists(namespc, uri_decoder.decoded());
526}
527
528bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
529 const auto path = ToWinAPIFilePath(name);
530 int flags = O_RDONLY | O_CREAT;
531 if (exclusive) {
532 flags |= O_EXCL;
533 }
534 int fd = _wopen(path.get(), flags, 0666);
535 if (fd < 0) {
536 return false;
537 }
538 return (Utils::Close(fd) == 0);
539}
540
541// This structure is needed for creating and reading Junctions.
542typedef struct _REPARSE_DATA_BUFFER {
543 ULONG ReparseTag;
544 USHORT ReparseDataLength;
545 USHORT Reserved;
546
547 union {
548 struct {
549 USHORT SubstituteNameOffset;
550 USHORT SubstituteNameLength;
551 USHORT PrintNameOffset;
552 USHORT PrintNameLength;
553 ULONG Flags;
554 WCHAR PathBuffer[1];
555 } SymbolicLinkReparseBuffer;
556
557 struct {
558 USHORT SubstituteNameOffset;
559 USHORT SubstituteNameLength;
560 USHORT PrintNameOffset;
561 USHORT PrintNameLength;
562 WCHAR PathBuffer[1];
563 } MountPointReparseBuffer;
564
565 struct {
566 UCHAR DataBuffer[1];
567 } GenericReparseBuffer;
568 };
569} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
570
571bool File::CreateLink(Namespace* namespc,
572 const char* utf8_name,
573 const char* utf8_target) {
574 const auto name = ToWinAPIFilePath(utf8_name);
575
576 std::unique_ptr<wchar_t[]> target;
577 bool target_is_directory;
578 if (File::IsAbsolutePath(utf8_target)) {
579 target = ToWinAPIFilePath(utf8_target);
580 target_is_directory =
581 File::GetType(target.get(), /*follow_links=*/true) == kIsDirectory;
582 } else {
583 // The path of `target` is relative to `name`.
584 //
585 // To determine if `target` is a file or directory, we need to calculate
586 // either its absolute path or its path relative to the current working
587 // directory.
588 //
589 // For example:
590 //
591 // name= C:\A\B\Link ..\..\Link ..\..\Link
592 // target= MyFile MyFile ..\Dir\MyFile
593 // --------------------------------------------------------------------
594 // target_path= C:\A\B\MyFile ..\..\MyFile ..\..\..\Dir\MyFile
595 //
596 // The transformation steps are:
597 // 1. target_path := name ..\..\Link
598 // 2. target_path := remove_file(target_path) ..\..\
599 // 3. target_path := combine(target_path, target) ..\..\..\Dir\MyFile
600 target = Utf8ToWideChar(utf8_target);
601
602 // 1. target_path := name
603 intptr_t target_path_max_length =
604 wcslen(name.get()) + wcslen(target.get()) + 2;
605 auto target_path = std::make_unique<wchar_t[]>(target_path_max_length);
606 wcscpy_s(target_path.get(), target_path_max_length, name.get());
607
608 // 2. target_path := remove_file(target_path)
609 HRESULT remove_result =
610 PathCchRemoveFileSpec(target_path.get(), target_path_max_length);
611 if (remove_result == S_FALSE) {
612 // If the file component could not be removed, then `name` is
613 // top-level, like "C:\" or "/". Attempts to create files at those paths
614 // will fail with ERROR_ACCESS_DENIED.
616 return false;
617 } else if (remove_result != S_OK) {
618 SetLastError(remove_result);
619 return false;
620 }
621
622 // 3. target_path := combine(target_path, target)
623 HRESULT combine_result = PathCchCombineEx(
624 target_path.get(), target_path_max_length, target_path.get(),
625 target.get(), PATHCCH_ALLOW_LONG_PATHS);
626 if (combine_result != S_OK) {
627 SetLastError(combine_result);
628 return false;
629 }
630
631 target_is_directory =
632 File::GetType(target_path.get(), /*follow_links=*/true) == kIsDirectory;
633 }
634
635 DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
636 if (target_is_directory) {
637 flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
638 }
639 int create_status = CreateSymbolicLinkW(name.get(), target.get(), flags);
640
641 // If running on a Windows 10 build older than 14972, an invalid parameter
642 // error will be returned when trying to use the
643 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag. Retry without the flag.
644 if ((create_status == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)) {
645 flags &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
646 create_status = CreateSymbolicLinkW(name.get(), target.get(), flags);
647 }
648
649 return (create_status != 0);
650}
651
652bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
653 int pipe_fds[2];
654 int status = _pipe(pipe_fds, 4096, _O_BINARY);
655 if (status != 0) {
656 return false;
657 }
658 *readPipe = OpenFD(pipe_fds[0]);
659 *writePipe = OpenFD(pipe_fds[1]);
660 return true;
661}
662
663bool File::Delete(Namespace* namespc, const char* name) {
664 const auto path = ToWinAPIFilePath(name);
665 int status = _wremove(path.get());
666 return status != -1;
667}
668
669static bool DeleteLinkHelper(const wchar_t* path) {
670 bool result = false;
671 DWORD attributes = GetFileAttributesW(path);
672 if ((attributes == INVALID_FILE_ATTRIBUTES) ||
673 ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0)) {
674 SetLastError(ERROR_NOT_A_REPARSE_POINT);
675 return false;
676 }
677 if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
678 // It's a junction, which is a special type of directory, or a symbolic
679 // link to a directory. Remove the directory.
680 result = (RemoveDirectoryW(path) != 0);
681 } else {
682 // Symbolic link to a file. Remove the file.
683 result = (DeleteFileW(path) != 0);
684 }
685 return result;
686}
687
688bool File::DeleteLink(Namespace* namespc, const char* name) {
689 const auto path = ToWinAPIFilePath(name);
690 return DeleteLinkHelper(path.get());
691}
692
693static bool RenameHelper(File::Type expected,
694 const char* old_name,
695 const char* new_name) {
696 const auto old_path = ToWinAPIFilePath(old_name);
697 File::Type type = File::GetType(old_path.get(), /*follow_links=*/false);
698 if (type != expected) {
700 return false;
701 }
702 const auto new_path = ToWinAPIFilePath(new_name);
703 DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING;
704
705 // Symbolic links (e.g. produced by Link.create) to directories on Windows
706 // appear as special directories. MoveFileExW's MOVEFILE_REPLACE_EXISTING
707 // does not allow for replacement of directories, so we need to remove it
708 // before renaming.
709 if ((Directory::Exists(new_path.get()) == Directory::EXISTS) &&
710 (File::GetType(new_path.get(), /*follow_links=*/false) ==
711 File::kIsLink)) {
712 // Bail out if the DeleteLink call fails.
713 if (!DeleteLinkHelper(new_path.get())) {
714 return false;
715 }
716 }
717 int move_status = MoveFileExW(old_path.get(), new_path.get(), flags);
718 return (move_status != 0);
719}
720
721bool File::Rename(Namespace* namespc,
722 const char* old_name,
723 const char* new_name) {
724 return RenameHelper(File::kIsFile, old_name, new_name);
725}
726
727bool File::RenameLink(Namespace* namespc,
728 const char* old_name,
729 const char* new_name) {
730 return RenameHelper(File::kIsLink, old_name, new_name);
731}
732
733static std::unique_ptr<wchar_t[]> GetDirectoryPath(
734 const std::unique_ptr<wchar_t[]>& path) {
735 for (intptr_t i = wcslen(path.get()) - 1; i >= 0; --i) {
736 if (path.get()[i] == '\\' || path.get()[i] == '/') {
737 auto result = std::make_unique<wchar_t[]>(i + 1);
738 wcsncpy(result.get(), path.get(), i);
739 return result;
740 }
741 }
742 return nullptr;
743}
744
745static void FreeUUID(wchar_t* ptr) {
746 RpcStringFreeW(&ptr);
747}
748
749static std::unique_ptr<wchar_t, decltype(FreeUUID)*> GenerateUUIDString() {
750 UUID uuid;
751 RPC_STATUS status = UuidCreateSequential(&uuid);
752 if ((status != RPC_S_OK) && (status != RPC_S_UUID_LOCAL_ONLY)) {
753 return {nullptr, nullptr};
754 }
755 wchar_t* uuid_string;
756 status = UuidToStringW(&uuid, &uuid_string);
757 if (status != RPC_S_OK) {
758 return {nullptr, nullptr};
759 }
760
761 return {uuid_string, &FreeUUID};
762}
763
764// This function will copy the |src| file to a temporary file in the
765// directory where |dest| resides and returns the path of temp file.
766static std::unique_ptr<wchar_t[]> CopyIntoTempFile(
767 const std::unique_ptr<wchar_t[]>& src,
768 const std::unique_ptr<wchar_t[]>& dest) {
769 const auto dir = GetDirectoryPath(dest);
770 if (dir == nullptr) {
771 return nullptr;
772 }
773
774 uint32_t suffix_bytes = 0;
775 const int kSuffixSize = sizeof(suffix_bytes);
776 if (Crypto::GetRandomBytes(kSuffixSize,
777 reinterpret_cast<uint8_t*>(&suffix_bytes))) {
778 const size_t file_path_buf_size = wcslen(dir.get()) + 8 + 1;
779 auto file_path = std::make_unique<wchar_t[]>(file_path_buf_size);
780 swprintf(file_path.get(), file_path_buf_size, L"%s%x", dir.get(),
781 suffix_bytes);
782
783 if (CopyFileExW(src.get(), file_path.get(), nullptr, nullptr, nullptr, 0) !=
784 0) {
785 return file_path;
786 }
787
788 // If CopyFileExW() fails to copy to a temp file with random hex, fall
789 // back to copy to a uuid temp file.
790 }
791
792 const auto uuid_str = GenerateUUIDString();
793 if (uuid_str == nullptr) {
794 return nullptr;
795 }
796
797 const size_t file_path_buf_size =
798 wcslen(dir.get()) + wcslen(uuid_str.get()) + 1;
799 auto file_path = std::make_unique<wchar_t[]>(file_path_buf_size);
800 swprintf(file_path.get(), file_path_buf_size, L"%s%s", dir.get(),
801 uuid_str.get());
802
803 if (CopyFileExW(src.get(), file_path.get(), nullptr, nullptr, nullptr, 0) !=
804 0) {
805 return file_path;
806 }
807
808 return nullptr;
809}
810
811bool File::Copy(Namespace* namespc,
812 const char* old_name,
813 const char* new_name) {
814 // We are going to concatenate old path with temporary file names in
815 // CopyIntoTempFile so we force long prefix no matter what.
816 const auto old_path = ToWinAPIFilePath(old_name, /*force_long_prefix=*/true);
817 const auto new_path = ToWinAPIFilePath(new_name);
818 File::Type type = GetType(old_path.get(), /*follow_links=*/false);
819 if (type != kIsFile) {
821 return false;
822 }
823
824 const auto temp_file = CopyIntoTempFile(old_path, new_path);
825 if (temp_file == nullptr) {
826 // If temp file creation fails, fall back on doing a direct copy.
827 return CopyFileExW(old_path.get(), new_path.get(), nullptr, nullptr,
828 nullptr, 0) != 0;
829 }
830
831 // Remove the existing file. Otherwise, renaming will fail.
832 if (FileExists(new_path.get())) {
833 DeleteFileW(new_path.get());
834 }
835
836 if (!MoveFileW(temp_file.get(), new_path.get())) {
838 DeleteFileW(temp_file.get());
840 return false;
841 }
842
843 return true;
844}
845
846int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
847 struct __stat64 st;
848 const auto path = ToWinAPIFilePath(name);
849 if (!StatHelper(path.get(), &st)) {
850 return -1;
851 }
852 return st.st_size;
853}
854
855const char* File::LinkTarget(Namespace* namespc,
856 const char* pathname,
857 char* dest,
858 int dest_size) {
859 const auto path = ToWinAPIFilePath(pathname);
860 HANDLE dir_handle = CreateFileW(
861 path.get(), GENERIC_READ,
862 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
863 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
864 nullptr);
865 if (dir_handle == INVALID_HANDLE_VALUE) {
866 return nullptr;
867 }
868
869 // Allocate a buffer for regular paths (smaller than MAX_PATH). If buffer is
870 // too small for a long path, allocate a bigger buffer and try again.
871 int buffer_size =
872 sizeof(REPARSE_DATA_BUFFER) + (MAX_PATH + 1) * sizeof(WCHAR);
873 REPARSE_DATA_BUFFER* buffer =
874 reinterpret_cast<REPARSE_DATA_BUFFER*>(Dart_ScopeAllocate(buffer_size));
875 DWORD received_bytes; // Value is not used.
876 int result = DeviceIoControl(dir_handle, FSCTL_GET_REPARSE_POINT, nullptr, 0,
877 buffer, buffer_size, &received_bytes, nullptr);
878 if (result == 0) {
880 // If ERROR_MORE_DATA is thrown, the target path exceeds the size limit. A
881 // bigger buffer will be required.
882 if (error == ERROR_MORE_DATA) {
883 // Allocate a bigger buffer with MAX_LONG_PATH
885 sizeof(REPARSE_DATA_BUFFER) + (MAX_LONG_PATH + 1) * sizeof(WCHAR);
886 buffer = reinterpret_cast<REPARSE_DATA_BUFFER*>(
888 result = DeviceIoControl(dir_handle, FSCTL_GET_REPARSE_POINT, nullptr, 0,
889 buffer, buffer_size, &received_bytes, nullptr);
890 if (result == 0) {
891 // Overwrite the ERROR_MORE_DATA.
893 }
894 }
895 if (result == 0) {
896 CloseHandle(dir_handle);
898 return nullptr;
899 }
900 }
901 if (CloseHandle(dir_handle) == 0) {
902 return nullptr;
903 }
904
905 wchar_t* target;
906 size_t target_offset;
907 size_t target_length;
908 if (buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
909 target = buffer->MountPointReparseBuffer.PathBuffer;
910 target_offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
911 target_length = buffer->MountPointReparseBuffer.SubstituteNameLength;
912 } else if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
913 target = buffer->SymbolicLinkReparseBuffer.PathBuffer;
914 target_offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset;
915 target_length = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
916 } else { // Not a junction or a symbolic link.
917 SetLastError(ERROR_NOT_A_REPARSE_POINT);
918 return nullptr;
919 }
920
921 target_offset /= sizeof(wchar_t); // Offset and length are in bytes.
922 target_length /= sizeof(wchar_t);
923 target += target_offset;
924 // Remove "\??\" from beginning of target.
925 if ((target_length > 4) && (wcsncmp(L"\\??\\", target, 4) == 0)) {
926 target += 4;
927 target_length -= 4;
928 }
929 int utf8_length = WideCharToMultiByte(CP_UTF8, 0, target, target_length,
930 nullptr, 0, nullptr, nullptr);
931 if (dest_size > 0 && dest_size <= utf8_length) {
932 return nullptr;
933 }
934 if (dest == nullptr) {
935 dest = DartUtils::ScopedCString(utf8_length + 1);
936 }
937 if (0 == WideCharToMultiByte(CP_UTF8, 0, target, target_length, dest,
938 utf8_length, nullptr, nullptr)) {
939 return nullptr;
940 }
941 dest[utf8_length] = '\0';
942 return dest;
943}
944
945void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
946 const auto path = ToWinAPIFilePath(name);
947 File::Type type = GetType(path.get(), /*follow_links=*/true);
948 data[kType] = type;
949 if (type != kDoesNotExist) {
950 struct _stat64 st;
951 int stat_status = _wstat64(path.get(), &st);
952 if (stat_status == 0) {
953 data[kCreatedTime] = st.st_ctime * 1000;
954 data[kModifiedTime] = st.st_mtime * 1000;
955 data[kAccessedTime] = st.st_atime * 1000;
956 data[kMode] = st.st_mode;
957 data[kSize] = st.st_size;
958 } else {
960 }
961 }
962}
963
964time_t File::LastAccessed(Namespace* namespc, const char* name) {
965 struct __stat64 st;
966 const auto path = ToWinAPIFilePath(name);
967 if (!StatHelper(path.get(), &st)) {
968 return -1;
969 }
970 return st.st_atime;
971}
972
973time_t File::LastModified(Namespace* namespc, const char* name) {
974 struct __stat64 st;
975 const auto path = ToWinAPIFilePath(name);
976 if (!StatHelper(path.get(), &st)) {
977 return -1;
978 }
979 return st.st_mtime;
980}
981
982bool File::SetLastAccessed(Namespace* namespc,
983 const char* name,
984 int64_t millis) {
985 struct __stat64 st;
986 const auto path = ToWinAPIFilePath(name);
987 if (!StatHelper(path.get(), &st)) { // Checks that it is a file.
988 return false;
989 }
990
991 // _utime and related functions set the access and modification times of the
992 // affected file. Even if the specified modification time is not changed
993 // from the current value, _utime will trigger a file modification event
994 // (e.g. ReadDirectoryChangesW will report the file as modified).
995 //
996 // So set the file access time directly using SetFileTime.
997 FILETIME at = GetFiletimeFromMillis(millis);
998 HANDLE file_handle =
999 CreateFileW(path.get(), FILE_WRITE_ATTRIBUTES,
1000 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1001 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1002 if (file_handle == INVALID_HANDLE_VALUE) {
1003 return false;
1004 }
1005 bool result = SetFileTime(file_handle, nullptr, &at, nullptr);
1006 CloseHandle(file_handle);
1007 return result;
1008}
1009
1010bool File::SetLastModified(Namespace* namespc,
1011 const char* name,
1012 int64_t millis) {
1013 // First get the current times.
1014 struct __stat64 st;
1015 const auto path = ToWinAPIFilePath(name);
1016 if (!StatHelper(path.get(), &st)) {
1017 return false;
1018 }
1019
1020 // Set the new time:
1021 struct __utimbuf64 times;
1022 times.actime = st.st_atime;
1023 times.modtime = millis / kMillisecondsPerSecond;
1024 return _wutime64(path.get(), &times) == 0;
1025}
1026
1027// Keep this function synchronized with the behavior
1028// of `FileSystemEntity.isAbsolute` in file_system_entity.dart.
1029bool File::IsAbsolutePath(const char* pathname) {
1030 if (pathname == nullptr) return false;
1031 char first = pathname[0];
1032 char second = pathname[1];
1033 if (first == '\\' && second == '\\') return true;
1034 if (second != ':') return false;
1035 first |= 0x20;
1036 char third = pathname[2];
1037 return (first >= 'a') && (first <= 'z') && (third == '\\' || third == '/');
1038}
1039
1040const char* File::GetCanonicalPath(Namespace* namespc,
1041 const char* pathname,
1042 char* dest,
1043 int dest_size) {
1044 const auto path = ToWinAPIFilePath(pathname);
1045 HANDLE file_handle =
1046 CreateFileW(path.get(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
1047 FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1048 if (file_handle == INVALID_HANDLE_VALUE) {
1049 return nullptr;
1050 }
1051 wchar_t dummy_buffer[1];
1052 int required_size =
1053 GetFinalPathNameByHandle(file_handle, dummy_buffer, 0, VOLUME_NAME_DOS);
1054 if (required_size == 0) {
1056 CloseHandle(file_handle);
1058 return nullptr;
1059 }
1060
1061 const auto canonical_path = std::make_unique<wchar_t[]>(required_size);
1062 int result_size = GetFinalPathNameByHandle(file_handle, canonical_path.get(),
1063 required_size, VOLUME_NAME_DOS);
1064 ASSERT(result_size <= required_size - 1);
1065 CloseHandle(file_handle);
1066
1067 // Remove leading \\?\ since it is only to overcome MAX_PATH limitation.
1068 // Leave it if input used it though.
1069 int offset = 0;
1070 if ((result_size > 4) &&
1071 (wcsncmp(canonical_path.get(), L"\\\\?\\", 4) == 0) &&
1072 (strncmp(pathname, "\\\\?\\", 4) != 0)) {
1073 if ((result_size > 8) &&
1074 (wcsncmp(canonical_path.get(), L"\\\\?\\UNC\\", 8) == 0)) {
1075 // Leave '\\?\UNC\' prefix intact - stripping it makes invalid UNC name.
1076 } else {
1077 offset = 4;
1078 }
1079 }
1080 int utf8_size = WideCharToMultiByte(CP_UTF8, 0, canonical_path.get() + offset,
1081 -1, nullptr, 0, nullptr, nullptr);
1082 if (dest == nullptr) {
1083 dest = DartUtils::ScopedCString(utf8_size);
1084 dest_size = utf8_size;
1085 }
1086 if (dest_size != 0) {
1087 ASSERT(utf8_size <= dest_size);
1088 }
1089 if (0 == WideCharToMultiByte(CP_UTF8, 0, canonical_path.get() + offset, -1,
1090 dest, dest_size, nullptr, nullptr)) {
1091 return nullptr;
1092 }
1093 return dest;
1094}
1095
1096const char* File::PathSeparator() {
1097 // This is already UTF-8 encoded.
1098 return "\\";
1099}
1100
1102 // This is already UTF-8 encoded.
1103 return "\\\\";
1104}
1105
1107 // Treat all stdio handles as pipes. The Windows event handler and
1108 // socket code will handle the different handle types.
1109 return kPipe;
1110}
1111
1112File::Type File::GetType(const wchar_t* path, bool follow_links) {
1113 DWORD attributes = GetFileAttributesW(path);
1114 if (attributes == INVALID_FILE_ATTRIBUTES) {
1115 return File::kDoesNotExist;
1116 } else if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
1117 if (follow_links) {
1118 HANDLE target_handle = CreateFileW(
1119 path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1120 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1121 if (target_handle == INVALID_HANDLE_VALUE) {
1122 return File::kDoesNotExist;
1123 } else {
1124 BY_HANDLE_FILE_INFORMATION info;
1125 if (!GetFileInformationByHandle(target_handle, &info)) {
1126 CloseHandle(target_handle);
1127 return File::kDoesNotExist;
1128 }
1129 CloseHandle(target_handle);
1130 return ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1132 : File::kIsFile;
1133 }
1134 } else {
1135 return File::kIsLink;
1136 }
1137 } else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
1138 return File::kIsDirectory;
1139 }
1140 return File::kIsFile;
1141}
1142
1143File::Type File::GetType(Namespace* namespc,
1144 const char* name,
1145 bool follow_links) {
1146 const auto path = ToWinAPIFilePath(name);
1147 return GetType(path.get(), follow_links);
1148}
1149
1150File::Identical File::AreIdentical(Namespace* namespc_1,
1151 const char* file_1,
1152 Namespace* namespc_2,
1153 const char* file_2) {
1154 USE(namespc_1);
1155 USE(namespc_2);
1156 BY_HANDLE_FILE_INFORMATION file_info[2];
1157 const std::unique_ptr<wchar_t[]> file_names[2] = {ToWinAPIFilePath(file_1),
1158 ToWinAPIFilePath(file_2)};
1159 for (int i = 0; i < 2; ++i) {
1160 HANDLE file_handle = CreateFileW(
1161 file_names[i].get(), 0,
1162 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
1163 OPEN_EXISTING,
1164 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
1165 if (file_handle == INVALID_HANDLE_VALUE) {
1166 return File::kError;
1167 }
1168 int result = GetFileInformationByHandle(file_handle, &file_info[i]);
1169 if (result == 0) {
1171 CloseHandle(file_handle);
1173 return File::kError;
1174 }
1175 if (CloseHandle(file_handle) == 0) {
1176 return File::kError;
1177 }
1178 }
1179 if ((file_info[0].dwVolumeSerialNumber ==
1180 file_info[1].dwVolumeSerialNumber) &&
1181 (file_info[0].nFileIndexHigh == file_info[1].nFileIndexHigh) &&
1182 (file_info[0].nFileIndexLow == file_info[1].nFileIndexLow)) {
1183 return kIdentical;
1184 } else {
1185 return kDifferent;
1186 }
1187}
1188
1189} // namespace bin
1190} // namespace dart
1191
1192#endif // defined(DART_HOST_OS_WINDOWS)
static SkISize times(const SkISize &size, float factor)
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)
#define UNREACHABLE()
Definition assert.h:248
#define MAX_LONG_PATH
Definition utils_win.h:15
static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static int32_t Low32Bits(int64_t value)
Definition utils.h:354
static char * StrDup(const char *s)
static CStringUniquePtr CreateCStringUniquePtr(char *str)
Definition utils.cc:257
static int32_t High32Bits(int64_t value)
Definition utils.h:358
static T Minimum(T x, T y)
Definition utils.h:21
std::unique_ptr< char, decltype(std::free) * > CStringUniquePtr
Definition utils.h:644
static size_t Read(int filedes, void *buf, size_t nbyte)
static int Close(int fildes)
static bool GetRandomBytes(intptr_t count, uint8_t *buffer)
static char * ScopedCString(intptr_t length)
Definition dartutils.h:224
static ExistsResult Exists(Namespace *namespc, const char *path)
static bool CreatePipe(Namespace *namespc, File **readPipe, File **writePipe)
static bool DeleteLink(Namespace *namespc, const char *path)
static bool IsAbsolutePath(const char *path)
MappedMemory * Map(MapType type, int64_t position, int64_t length, void *start=nullptr)
static const char * GetCanonicalPath(Namespace *namespc, const char *path, char *dest=nullptr, int dest_size=0)
int64_t Position()
int64_t Read(void *buffer, int64_t num_bytes)
static bool SetLastAccessed(Namespace *namespc, const char *path, int64_t millis)
static Utils::CStringUniquePtr UriToPath(const char *uri)
static File * OpenUri(Namespace *namespc, const char *uri, FileOpenMode mode)
@ kDoesNotExist
Definition file.h:81
static bool SetLastModified(Namespace *namespc, const char *path, int64_t millis)
static time_t LastModified(Namespace *namespc, const char *path)
static void Stat(Namespace *namespc, const char *path, int64_t *data)
static const char * PathSeparator()
static bool Create(Namespace *namespc, const char *path, bool exclusive)
bool VPrint(const char *format, va_list args)
static bool Rename(Namespace *namespc, const char *old_path, const char *new_path)
static bool Delete(Namespace *namespc, const char *path)
static const char * StringEscapedPathSeparator()
bool WriteFully(const void *buffer, int64_t num_bytes)
static int64_t LengthFromPath(Namespace *namespc, const char *path)
bool ReadFully(void *buffer, int64_t num_bytes)
static bool Copy(Namespace *namespc, const char *old_path, const char *new_path)
static time_t LastAccessed(Namespace *namespc, const char *path)
intptr_t GetFD()
bool result
Definition file.h:170
int64_t Write(const void *buffer, int64_t num_bytes)
static bool Exists(Namespace *namespc, const char *path)
static File * Open(Namespace *namespc, const char *path, FileOpenMode mode)
bool Lock(LockType lock, int64_t start, int64_t end)
bool SetPosition(int64_t position)
static Identical AreIdentical(Namespace *namespc_1, const char *file_1, Namespace *namespc_2, const char *file_2)
int64_t Length()
static File * OpenFD(int fd)
static const char * LinkTarget(Namespace *namespc, const char *pathname, char *dest=nullptr, int dest_size=0)
@ kLockBlockingShared
Definition file.h:113
@ kLockBlockingExclusive
Definition file.h:114
static bool CreateLink(Namespace *namespc, const char *path, const char *target)
static bool ExistsUri(Namespace *namespc, const char *uri)
static File * OpenStdio(int fd)
static StdioHandleType GetStdioHandleType(int fd)
static Type GetType(Namespace *namespc, const char *path, bool follow_links)
bool Truncate(int64_t length)
static bool RenameLink(Namespace *namespc, const char *old_path, const char *new_path)
#define ASSERT(E)
#define MAX_DIRECTORY_PATH
Definition file_win.h:14
FlutterSemanticsFlag flags
glong glong end
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
uint32_t uint32_t * format
uint32_t * target
size_t length
void WriteFile(const void *buffer, intptr_t num_bytes, void *stream)
std::unique_ptr< wchar_t[]> Utf8ToWideChar(const char *path)
FILETIME GetFiletimeFromMillis(int64_t millis)
std::unique_ptr< wchar_t[]> ToWinAPIDirectoryPath(const char *path, bool force_long_prefix=false)
constexpr int64_t kMaxInt64
Definition globals.h:486
const char *const name
void * malloc(size_t size)
Definition allocation.cc:19
DART_EXPORT uint8_t * Dart_ScopeAllocate(intptr_t size)
constexpr intptr_t kMillisecondsPerSecond
Definition globals.h:560
static void USE(T &&)
Definition globals.h:618
static int8_t data[kExtLength]
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 Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition switches.h:145
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
bool FileExists(const fml::UniqueFD &base_directory, const char *path)
const myers::Point & get(const myers::Segment &)
dest
Definition zip.py:79
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition globals.h:581
fuchsia::ui::composition::ParentViewportWatcherHandle handle_
Point offset
int BOOL
#define INVALID_HANDLE_VALUE
WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode)
struct _FILETIME FILETIME
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
void * HANDLE
struct _OVERLAPPED OVERLAPPED
DWORD ULONG
#define MAX_PATH
unsigned long DWORD
#define ERROR_FILE_NOT_FOUND
#define ERROR_ACCESS_DENIED