Flutter Engine
The Flutter Engine
file_linux.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_LINUX) || defined(DART_HOST_OS_ANDROID)
7
8#include "bin/file.h"
9
10#include <errno.h> // NOLINT
11#include <fcntl.h> // NOLINT
12#include <libgen.h> // NOLINT
13#include <sys/mman.h> // NOLINT
14#include <sys/sendfile.h> // NOLINT
15#include <sys/stat.h> // NOLINT
16#include <sys/types.h> // NOLINT
17#include <unistd.h> // NOLINT
18#include <utime.h> // NOLINT
19
20#include "bin/builtin.h"
21#include "bin/fdutils.h"
22#include "bin/namespace.h"
24#include "platform/syslog.h"
25#include "platform/utils.h"
26
27namespace dart {
28namespace bin {
29
30class FileHandle {
31 public:
32 explicit FileHandle(int fd) : fd_(fd) {}
33 ~FileHandle() {}
34 int fd() const { return fd_; }
35 void set_fd(int fd) { fd_ = fd; }
36
37 private:
38 int fd_;
39
40 DISALLOW_COPY_AND_ASSIGN(FileHandle);
41};
42
43File::~File() {
44 if (!IsClosed() && (handle_->fd() != STDOUT_FILENO) &&
45 (handle_->fd() != STDERR_FILENO)) {
46 Close();
47 }
48 delete handle_;
49}
50
51void File::Close() {
52 ASSERT(handle_->fd() >= 0);
53 if (handle_->fd() == STDOUT_FILENO) {
54 // If stdout, redirect fd to /dev/null.
55 int null_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY));
56 ASSERT(null_fd >= 0);
57 VOID_TEMP_FAILURE_RETRY(dup2(null_fd, handle_->fd()));
58 close(null_fd);
59 } else {
60 int err = close(handle_->fd());
61 if (err != 0) {
62 const int kBufferSize = 1024;
63 char error_buf[kBufferSize];
64 Syslog::PrintErr("%s\n", Utils::StrError(errno, error_buf, kBufferSize));
65 }
66 }
67 handle_->set_fd(kClosedFd);
68}
69
70intptr_t File::GetFD() {
71 return handle_->fd();
72}
73
74bool File::IsClosed() {
75 return handle_->fd() == kClosedFd;
76}
77
78MappedMemory* File::Map(MapType type,
79 int64_t position,
80 int64_t length,
81 void* start) {
82 ASSERT(handle_->fd() >= 0);
83 ASSERT(length > 0);
84 void* hint = nullptr;
85 int prot = PROT_NONE;
86 int flags = MAP_PRIVATE;
87 switch (type) {
88 case kReadOnly:
89 prot = PROT_READ;
90 break;
91 case kReadExecute:
92 // Try to allocate near the VM's binary.
93 hint = reinterpret_cast<void*>(&Dart_Initialize);
94 prot = PROT_READ | PROT_EXEC;
95 break;
96 case kReadWrite:
97 prot = PROT_READ | PROT_WRITE;
98 break;
99 }
100 if (start != nullptr) {
101 hint = start;
102 flags |= MAP_FIXED;
103 }
104 void* addr = mmap(hint, length, prot, flags, handle_->fd(), position);
105
106#if defined(DART_HOST_OS_LINUX)
107 // On WSL 1 trying to allocate memory close to the binary by supplying a hint
108 // fails with ENOMEM for unclear reason. Some reports suggest that this might
109 // be related to the alignment of the hint but aligning it by 64Kb does not
110 // make the issue go away in our experiments. Instead just retry without any
111 // hint.
112 if (addr == MAP_FAILED && hint != nullptr && start == nullptr &&
113 Utils::IsWindowsSubsystemForLinux()) {
114 addr = mmap(nullptr, length, prot, flags, handle_->fd(), position);
115 }
116#endif
117
118 if (addr == MAP_FAILED) {
119 return nullptr;
120 }
121
122 return new MappedMemory(addr, length, /*should_unmap=*/start == nullptr);
123}
124
125void MappedMemory::Unmap() {
126 int result = munmap(address_, size_);
127 ASSERT(result == 0);
128 address_ = nullptr;
129 size_ = 0;
130}
131
132int64_t File::Read(void* buffer, int64_t num_bytes) {
133 ASSERT(handle_->fd() >= 0);
134 return TEMP_FAILURE_RETRY(read(handle_->fd(), buffer, num_bytes));
135}
136
137int64_t File::Write(const void* buffer, int64_t num_bytes) {
138 ASSERT(handle_->fd() >= 0);
139 return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes));
140}
141
142bool File::VPrint(const char* format, va_list args) {
143 // Measure.
144 va_list measure_args;
145 va_copy(measure_args, args);
146 intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args);
147 va_end(measure_args);
148
149 char* buffer = reinterpret_cast<char*>(malloc(len + 1));
150
151 // Print.
152 va_list print_args;
153 va_copy(print_args, args);
154 Utils::VSNPrint(buffer, len + 1, format, print_args);
155 va_end(print_args);
156
157 bool result = WriteFully(buffer, len);
158 free(buffer);
159 return result;
160}
161
162int64_t File::Position() {
163 ASSERT(handle_->fd() >= 0);
164 return NO_RETRY_EXPECTED(lseek64(handle_->fd(), 0, SEEK_CUR));
165}
166
167bool File::SetPosition(int64_t position) {
168 ASSERT(handle_->fd() >= 0);
169 return NO_RETRY_EXPECTED(lseek64(handle_->fd(), position, SEEK_SET)) >= 0;
170}
171
172bool File::Truncate(int64_t length) {
173 ASSERT(handle_->fd() >= 0);
174 return TEMP_FAILURE_RETRY(ftruncate64(handle_->fd(), length) != -1);
175}
176
177bool File::Flush() {
178 ASSERT(handle_->fd() >= 0);
179 return NO_RETRY_EXPECTED(fsync(handle_->fd())) != -1;
180}
181
182bool File::Lock(File::LockType lock, int64_t start, int64_t end) {
183 ASSERT(handle_->fd() >= 0);
184 ASSERT((end == -1) || (end > start));
185 struct flock fl;
186 switch (lock) {
188 fl.l_type = F_UNLCK;
189 break;
192 fl.l_type = F_RDLCK;
193 break;
196 fl.l_type = F_WRLCK;
197 break;
198 default:
199 return false;
200 }
201 fl.l_whence = SEEK_SET;
202 fl.l_start = start;
203 fl.l_len = end == -1 ? 0 : end - start;
204 int cmd = F_SETLK;
205 if ((lock == File::kLockBlockingShared) ||
207 cmd = F_SETLKW;
208 }
209 return TEMP_FAILURE_RETRY(fcntl(handle_->fd(), cmd, &fl)) != -1;
210}
211
212int64_t File::Length() {
213 ASSERT(handle_->fd() >= 0);
214 struct stat64 st;
215 if (TEMP_FAILURE_RETRY(fstat64(handle_->fd(), &st)) == 0) {
216 return st.st_size;
217 }
218 return -1;
219}
220
221File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
222 UNREACHABLE();
223 return nullptr;
224}
225
226File* File::OpenFD(int fd) {
227 return new File(new FileHandle(fd));
228}
229
230File* File::Open(Namespace* namespc, const char* name, FileOpenMode mode) {
231 NamespaceScope ns(namespc, name);
232 // Report errors for non-regular files.
233 struct stat64 st;
234 if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
235 // Only accept regular files, character devices, and pipes.
236 if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) && !S_ISFIFO(st.st_mode)) {
237 errno = (S_ISDIR(st.st_mode)) ? EISDIR : ENOENT;
238 return nullptr;
239 }
240 }
241 int flags = O_RDONLY;
242 if ((mode & kWrite) != 0) {
243 ASSERT((mode & kWriteOnly) == 0);
244 flags = (O_RDWR | O_CREAT);
245 }
246 if ((mode & kWriteOnly) != 0) {
247 ASSERT((mode & kWrite) == 0);
248 flags = (O_WRONLY | O_CREAT);
249 }
250 if ((mode & kTruncate) != 0) {
251 flags = flags | O_TRUNC;
252 }
253 flags |= O_CLOEXEC;
254 const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666));
255 if (fd < 0) {
256 return nullptr;
257 }
258 if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
259 (((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
260 int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END));
261 if (position < 0) {
262 return nullptr;
263 }
264 }
265 return OpenFD(fd);
266}
267
268CStringUniquePtr File::UriToPath(const char* uri) {
269 const char* path =
270 (strlen(uri) >= 8 && strncmp(uri, "file:///", 8) == 0) ? uri + 7 : uri;
271 UriDecoder uri_decoder(path);
272 if (uri_decoder.decoded() == nullptr) {
273 errno = EINVAL;
274 return CStringUniquePtr(nullptr);
275 }
276 return CStringUniquePtr(strdup(uri_decoder.decoded()));
277}
278
279File* File::OpenUri(Namespace* namespc, const char* uri, FileOpenMode mode) {
280 auto path = UriToPath(uri);
281 if (path == nullptr) {
282 return nullptr;
283 }
284 return File::Open(namespc, path.get(), mode);
285}
286
287File* File::OpenStdio(int fd) {
288 return new File(new FileHandle(fd));
289}
290
291bool File::Exists(Namespace* namespc, const char* name) {
292 NamespaceScope ns(namespc, name);
293 struct stat64 st;
294 if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
295 // Everything but a directory and a link is a file to Dart.
296 return !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode);
297 } else {
298 return false;
299 }
300}
301
302bool File::ExistsUri(Namespace* namespc, const char* uri) {
303 auto path = UriToPath(uri);
304 if (path == nullptr) {
305 return false;
306 }
307 return File::Exists(namespc, path.get());
308}
309
310bool File::Create(Namespace* namespc, const char* name, bool exclusive) {
311 NamespaceScope ns(namespc, name);
312 int flags = O_RDONLY | O_CREAT | O_CLOEXEC;
313 if (exclusive) {
314 flags |= O_EXCL;
315 }
316 const int fd = TEMP_FAILURE_RETRY(openat64(ns.fd(), ns.path(), flags, 0666));
317 if (fd < 0) {
318 return false;
319 }
320 // File.create returns a File, so we shouldn't be giving the illusion that the
321 // call has created a file or that a file already exists if there is already
322 // an entity at the same path that is a directory or a link.
323 bool is_file = true;
324 struct stat64 st;
325 if (TEMP_FAILURE_RETRY(fstat64(fd, &st)) == 0) {
326 if (S_ISDIR(st.st_mode)) {
327 errno = EISDIR;
328 is_file = false;
329 } else if (S_ISLNK(st.st_mode)) {
330 errno = ENOENT;
331 is_file = false;
332 }
333 }
335 return is_file;
336}
337
338bool File::CreateLink(Namespace* namespc,
339 const char* name,
340 const char* target) {
341 NamespaceScope ns(namespc, name);
342 return NO_RETRY_EXPECTED(symlinkat(target, ns.fd(), ns.path())) == 0;
343}
344
345bool File::CreatePipe(Namespace* namespc, File** readPipe, File** writePipe) {
346 int pipe_fds[2];
347 int status = NO_RETRY_EXPECTED(pipe(pipe_fds));
348 if (status != 0) {
349 return false;
350 }
351 *readPipe = OpenFD(pipe_fds[0]);
352 *writePipe = OpenFD(pipe_fds[1]);
353 return true;
354}
355
356File::Type File::GetType(Namespace* namespc,
357 const char* name,
358 bool follow_links) {
359 NamespaceScope ns(namespc, name);
360 struct stat64 entry_info;
361 int stat_success;
362 if (follow_links) {
363 stat_success =
364 TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &entry_info, 0));
365 } else {
366 stat_success = TEMP_FAILURE_RETRY(
367 fstatat64(ns.fd(), ns.path(), &entry_info, AT_SYMLINK_NOFOLLOW));
368 }
369 if (stat_success == -1) {
370 return File::kDoesNotExist;
371 }
372 if (S_ISDIR(entry_info.st_mode)) {
373 return File::kIsDirectory;
374 }
375 if (S_ISREG(entry_info.st_mode)) {
376 return File::kIsFile;
377 }
378 if (S_ISLNK(entry_info.st_mode)) {
379 return File::kIsLink;
380 }
381 if (S_ISSOCK(entry_info.st_mode)) {
382 return File::kIsSock;
383 }
384 if (S_ISFIFO(entry_info.st_mode)) {
385 return File::kIsPipe;
386 }
387 return File::kDoesNotExist;
388}
389
390static void SetErrno(File::Type type) {
391 switch (type) {
393 errno = EISDIR;
394 break;
396 errno = ENOENT;
397 break;
398 default:
399 errno = EINVAL;
400 break;
401 }
402}
403
404static bool CheckTypeAndSetErrno(Namespace* namespc,
405 const char* name,
406 File::Type expected,
407 bool follow_links) {
408 File::Type actual = File::GetType(namespc, name, follow_links);
409 if (actual == expected) {
410 return true;
411 }
412 SetErrno(actual);
413 return false;
414}
415
416bool File::Delete(Namespace* namespc, const char* name) {
417 File::Type type = File::GetType(namespc, name, true);
418 if (type == kIsFile || type == kIsSock || type == kIsPipe) {
419 NamespaceScope ns(namespc, name);
420 return (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
421 }
422 SetErrno(type);
423 return false;
424}
425
426bool File::DeleteLink(Namespace* namespc, const char* name) {
427 NamespaceScope ns(namespc, name);
428 return CheckTypeAndSetErrno(namespc, name, kIsLink, false) &&
429 (NO_RETRY_EXPECTED(unlinkat(ns.fd(), ns.path(), 0)) == 0);
430}
431
432bool File::Rename(Namespace* namespc,
433 const char* old_path,
434 const char* new_path) {
435 File::Type type = File::GetType(namespc, old_path, true);
436 if (type == kIsFile || type == kIsSock || type == kIsPipe) {
437 NamespaceScope oldns(namespc, old_path);
438 NamespaceScope newns(namespc, new_path);
439 return (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
440 newns.path())) == 0);
441 }
442 SetErrno(type);
443 return false;
444}
445
446bool File::RenameLink(Namespace* namespc,
447 const char* old_path,
448 const char* new_path) {
449 NamespaceScope oldns(namespc, old_path);
450 NamespaceScope newns(namespc, new_path);
451 return CheckTypeAndSetErrno(namespc, old_path, kIsLink, false) &&
452 (NO_RETRY_EXPECTED(renameat(oldns.fd(), oldns.path(), newns.fd(),
453 newns.path())) == 0);
454}
455
456bool File::Copy(Namespace* namespc,
457 const char* old_path,
458 const char* new_path) {
459 File::Type type = File::GetType(namespc, old_path, true);
460 if (type != kIsFile && type != kIsSock && type != kIsPipe) {
461 SetErrno(type);
462 return false;
463 }
464 NamespaceScope oldns(namespc, old_path);
465 struct stat64 st;
466 if (TEMP_FAILURE_RETRY(fstatat64(oldns.fd(), oldns.path(), &st, 0)) != 0) {
467 return false;
468 }
469 const int old_fd = TEMP_FAILURE_RETRY(
470 openat64(oldns.fd(), oldns.path(), O_RDONLY | O_CLOEXEC));
471 if (old_fd < 0) {
472 return false;
473 }
474 NamespaceScope newns(namespc, new_path);
475 const int new_fd = TEMP_FAILURE_RETRY(
476 openat64(newns.fd(), newns.path(),
477 O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, st.st_mode));
478 if (new_fd < 0) {
479 close(old_fd);
480 return false;
481 }
482 int64_t offset = 0;
483 intptr_t result = 1;
484 while (result > 0) {
485 // Loop to ensure we copy everything, and not only up to 2GB.
486 result = NO_RETRY_EXPECTED(sendfile64(new_fd, old_fd, &offset, kMaxUint32));
487 }
488 // From sendfile man pages:
489 // Applications may wish to fall back to read(2)/write(2) in the case
490 // where sendfile() fails with EINVAL or ENOSYS.
491 if ((result < 0) && ((errno == EINVAL) || (errno == ENOSYS))) {
492 const intptr_t kBufferSize = 8 * KB;
493 uint8_t* buffer = reinterpret_cast<uint8_t*>(malloc(kBufferSize));
494 while ((result = TEMP_FAILURE_RETRY(read(old_fd, buffer, kBufferSize))) >
495 0) {
496 int wrote = TEMP_FAILURE_RETRY(write(new_fd, buffer, result));
497 if (wrote != result) {
498 result = -1;
499 break;
500 }
501 }
502 free(buffer);
503 }
504 int e = errno;
505 close(old_fd);
506 close(new_fd);
507 if (result < 0) {
508 VOID_NO_RETRY_EXPECTED(unlinkat(newns.fd(), newns.path(), 0));
509 errno = e;
510 return false;
511 }
512 return true;
513}
514
515static bool StatHelper(Namespace* namespc,
516 const char* name,
517 struct stat64* st) {
518 NamespaceScope ns(namespc, name);
519 if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), st, 0)) != 0) {
520 return false;
521 }
522 // Signal an error if it's a directory.
523 if (S_ISDIR(st->st_mode)) {
524 errno = EISDIR;
525 return false;
526 }
527 // Otherwise assume the caller knows what it's doing.
528 return true;
529}
530
531int64_t File::LengthFromPath(Namespace* namespc, const char* name) {
532 struct stat64 st;
533 if (!StatHelper(namespc, name, &st)) {
534 return -1;
535 }
536 return st.st_size;
537}
538
539static int64_t TimespecToMilliseconds(const struct timespec& t) {
540 return static_cast<int64_t>(t.tv_sec) * 1000L +
541 static_cast<int64_t>(t.tv_nsec) / 1000000L;
542}
543
544static void MillisecondsToTimespec(int64_t millis, struct timespec* t) {
545 ASSERT(t != nullptr);
546 t->tv_sec = millis / kMillisecondsPerSecond;
547 t->tv_nsec = (millis % kMillisecondsPerSecond) * 1000L;
548}
549
550void File::Stat(Namespace* namespc, const char* name, int64_t* data) {
551 NamespaceScope ns(namespc, name);
552 struct stat64 st;
553 if (TEMP_FAILURE_RETRY(fstatat64(ns.fd(), ns.path(), &st, 0)) == 0) {
554 if (S_ISREG(st.st_mode)) {
555 data[kType] = kIsFile;
556 } else if (S_ISDIR(st.st_mode)) {
558 } else if (S_ISLNK(st.st_mode)) {
559 data[kType] = kIsLink;
560 } else if (S_ISSOCK(st.st_mode)) {
561 data[kType] = kIsSock;
562 } else if (S_ISFIFO(st.st_mode)) {
563 data[kType] = kIsPipe;
564 } else {
566 }
567 data[kCreatedTime] = TimespecToMilliseconds(st.st_ctim);
568 data[kModifiedTime] = TimespecToMilliseconds(st.st_mtim);
569 data[kAccessedTime] = TimespecToMilliseconds(st.st_atim);
570 data[kMode] = st.st_mode;
571 data[kSize] = st.st_size;
572 } else {
574 }
575}
576
577time_t File::LastModified(Namespace* namespc, const char* name) {
578 struct stat64 st;
579 if (!StatHelper(namespc, name, &st)) {
580 return -1;
581 }
582 return st.st_mtime;
583}
584
585time_t File::LastAccessed(Namespace* namespc, const char* name) {
586 struct stat64 st;
587 if (!StatHelper(namespc, name, &st)) {
588 return -1;
589 }
590 return st.st_atime;
591}
592
593bool File::SetLastAccessed(Namespace* namespc,
594 const char* name,
595 int64_t millis) {
596 // First get the current times.
597 struct stat64 st;
598 if (!StatHelper(namespc, name, &st)) {
599 return false;
600 }
601
602 // Set the new time:
603 NamespaceScope ns(namespc, name);
604 struct timespec times[2];
605 MillisecondsToTimespec(millis, &times[0]);
606 times[1] = st.st_mtim;
607 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
608}
609
610bool File::SetLastModified(Namespace* namespc,
611 const char* name,
612 int64_t millis) {
613 // First get the current times.
614 struct stat64 st;
615 if (!StatHelper(namespc, name, &st)) {
616 return false;
617 }
618
619 // Set the new time:
620 NamespaceScope ns(namespc, name);
621 struct timespec times[2];
622 times[0] = st.st_atim;
623 MillisecondsToTimespec(millis, &times[1]);
624 return utimensat(ns.fd(), ns.path(), times, 0) == 0;
625}
626
627const char* File::LinkTarget(Namespace* namespc,
628 const char* name,
629 char* dest,
630 int dest_size) {
631 NamespaceScope ns(namespc, name);
632 struct stat64 link_stats;
633 const int status = TEMP_FAILURE_RETRY(
634 fstatat64(ns.fd(), ns.path(), &link_stats, AT_SYMLINK_NOFOLLOW));
635 if (status != 0) {
636 return nullptr;
637 }
638 if (!S_ISLNK(link_stats.st_mode)) {
639 errno = ENOENT;
640 return nullptr;
641 }
642 // Don't rely on the link_stats.st_size for the size of the link
643 // target. For some filesystems, e.g. procfs, this value is always
644 // 0. Also the link might have changed before the readlink call.
645 const int kBufferSize = PATH_MAX + 1;
646 char target[kBufferSize];
647 const int target_size =
648 TEMP_FAILURE_RETRY(readlinkat(ns.fd(), ns.path(), target, kBufferSize));
649 if (target_size <= 0) {
650 return nullptr;
651 }
652 if (dest == nullptr) {
653 dest = DartUtils::ScopedCString(target_size + 1);
654 } else {
655 ASSERT(dest_size > 0);
656 if (dest_size <= target_size) {
657 return nullptr;
658 }
659 }
660 memmove(dest, target, target_size);
661 dest[target_size] = '\0';
662 return dest;
663}
664
665bool File::IsAbsolutePath(const char* pathname) {
666 return (pathname != nullptr) && (pathname[0] == '/');
667}
668
669intptr_t File::ReadLinkInto(const char* pathname,
670 char* result,
671 size_t result_size) {
672 ASSERT(pathname != nullptr);
673 ASSERT(IsAbsolutePath(pathname));
674 struct stat64 link_stats;
675 if (TEMP_FAILURE_RETRY(lstat64(pathname, &link_stats)) != 0) {
676 return -1;
677 }
678 if (!S_ISLNK(link_stats.st_mode)) {
679 errno = ENOENT;
680 return -1;
681 }
682 size_t target_size =
683 TEMP_FAILURE_RETRY(readlink(pathname, result, result_size));
684 if (target_size <= 0) {
685 return -1;
686 }
687 // readlink returns non-zero terminated strings. Append.
688 if (target_size < result_size) {
689 result[target_size] = '\0';
690 target_size++;
691 }
692 return target_size;
693}
694
695const char* File::ReadLink(const char* pathname) {
696 // Don't rely on the link_stats.st_size for the size of the link
697 // target. For some filesystems, e.g. procfs, this value is always
698 // 0. Also the link might have changed before the readlink call.
699 const int kBufferSize = PATH_MAX + 1;
700 char target[kBufferSize];
701 size_t target_size = ReadLinkInto(pathname, target, kBufferSize);
702 if (target_size <= 0) {
703 return nullptr;
704 }
705 char* target_name = DartUtils::ScopedCString(target_size);
706 ASSERT(target_name != nullptr);
707 memmove(target_name, target, target_size);
708 return target_name;
709}
710
711const char* File::GetCanonicalPath(Namespace* namespc,
712 const char* name,
713 char* dest,
714 int dest_size) {
715 if (name == nullptr) {
716 return nullptr;
717 }
718 if (!Namespace::IsDefault(namespc)) {
719 // TODO(zra): There is no realpathat(). Also chasing a symlink might result
720 // in a path to something outside of the namespace, so canonicalizing paths
721 // would have to be done carefully. For now, don't do anything.
722 return name;
723 }
724 char* abs_path;
725 if (dest == nullptr) {
727 } else {
728 ASSERT(dest_size >= PATH_MAX);
729 }
730 ASSERT(dest != nullptr);
731 do {
732 abs_path = realpath(name, dest);
733 } while ((abs_path == nullptr) && (errno == EINTR));
734 ASSERT(abs_path == nullptr || IsAbsolutePath(abs_path));
735 ASSERT(abs_path == nullptr || (abs_path == dest));
736 return abs_path;
737}
738
739const char* File::PathSeparator() {
740 return "/";
741}
742
744 return "/";
745}
746
748 struct stat64 buf;
749 int result = TEMP_FAILURE_RETRY(fstat64(fd, &buf));
750 if (result == -1) {
751 return kTypeError;
752 }
753 if (S_ISCHR(buf.st_mode)) {
754 return kTerminal;
755 }
756 if (S_ISFIFO(buf.st_mode)) {
757 return kPipe;
758 }
759 if (S_ISSOCK(buf.st_mode)) {
760 return kSocket;
761 }
762 if (S_ISREG(buf.st_mode)) {
763 return kFile;
764 }
765 return kOther;
766}
767
768File::Identical File::AreIdentical(Namespace* namespc_1,
769 const char* file_1,
770 Namespace* namespc_2,
771 const char* file_2) {
772 struct stat64 file_1_info;
773 struct stat64 file_2_info;
774 int status;
775 {
776 NamespaceScope ns1(namespc_1, file_1);
777 status = TEMP_FAILURE_RETRY(
778 fstatat64(ns1.fd(), ns1.path(), &file_1_info, AT_SYMLINK_NOFOLLOW));
779 if (status == -1) {
780 return File::kError;
781 }
782 }
783 {
784 NamespaceScope ns2(namespc_2, file_2);
785 status = TEMP_FAILURE_RETRY(
786 fstatat64(ns2.fd(), ns2.path(), &file_2_info, AT_SYMLINK_NOFOLLOW));
787 if (status == -1) {
788 return File::kError;
789 }
790 }
791 return ((file_1_info.st_ino == file_2_info.st_ino) &&
792 (file_1_info.st_dev == file_2_info.st_dev))
795}
796
797} // namespace bin
798} // namespace dart
799
800#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
static SkISize times(const SkISize &size, float factor)
static bool read(SkStream *stream, void *buffer, size_t amount)
static const size_t kBufferSize
Definition: SkString.cpp:27
#define UNREACHABLE()
Definition: assert.h:248
GLenum type
static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
static int static int VSNPrint(char *str, size_t size, const char *format, va_list args)
static char * StrError(int err, char *buffer, size_t bufsize)
Definition: utils_android.h:40
static char * ScopedCString(intptr_t length)
Definition: dartutils.h:224
static void SaveErrorAndClose(intptr_t fd)
FileHandle(HANDLE handle)
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)
static CStringUniquePtr UriToPath(const char *uri)
@ kModifiedTime
Definition: file.h:100
@ kCreatedTime
Definition: file.h:99
@ kAccessedTime
Definition: file.h:101
int64_t Position()
int64_t Read(void *buffer, int64_t num_bytes)
static bool SetLastAccessed(Namespace *namespc, const char *path, int64_t millis)
static File * OpenUri(Namespace *namespc, const char *uri, FileOpenMode mode)
@ kIsDirectory
Definition: file.h:77
@ 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()
@ kDifferent
Definition: file.h:84
@ kIdentical
Definition: file.h:84
bool WriteFully(const void *buffer, int64_t num_bytes)
Definition: file_support.cc:55
static int64_t LengthFromPath(Namespace *namespc, const char *path)
@ kReadExecute
Definition: file.h:122
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)
@ kTypeError
Definition: file.h:93
static const char * ReadLink(const char *pathname)
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
@ kLockExclusive
Definition: file.h:112
static bool CreateLink(Namespace *namespc, const char *path, const char *target)
static bool ExistsUri(Namespace *namespc, const char *uri)
@ kWriteOnly
Definition: file.h:59
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 intptr_t ReadLinkInto(const char *pathname, char *result, size_t result_size)
static bool RenameLink(Namespace *namespc, const char *old_path, const char *new_path)
static bool IsDefault(Namespace *namespc)
Definition: namespace.cc:111
#define ASSERT(E)
FlutterSemanticsFlag flags
glong glong end
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
uint32_t uint32_t * format
uint32_t * target
size_t length
Definition: dart_vm.cc:33
const char *const name
CAllocUniquePtr< char > CStringUniquePtr
Definition: utils.h:31
void * malloc(size_t size)
Definition: allocation.cc:19
constexpr uint32_t kMaxUint32
Definition: globals.h:484
DART_EXPORT char * Dart_Initialize(Dart_InitializeParams *params)
constexpr intptr_t KB
Definition: globals.h:528
constexpr intptr_t kMillisecondsPerSecond
Definition: globals.h:560
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 buffer
Definition: switches.h:126
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
char * strdup(const char *str1)
dest
Definition: zip.py:79
#define PATH_MAX
Definition: globals.h:708
fuchsia::ui::composition::ParentViewportWatcherHandle handle_
#define NO_RETRY_EXPECTED(expression)
#define VOID_TEMP_FAILURE_RETRY(expression)
#define VOID_NO_RETRY_EXPECTED(expression)
#define TEMP_FAILURE_RETRY(expression)
void write(SkWStream *wStream, const T &text)
Definition: skqp.cpp:188
SeparatedVector2 offset