Flutter Engine
file_posix.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 <dirent.h>
8 #include <fcntl.h>
9 #include <sys/mman.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12 
13 #include <memory>
14 #include <sstream>
15 
16 #include "flutter/fml/eintr_wrapper.h"
17 #include "flutter/fml/logging.h"
18 #include "flutter/fml/mapping.h"
19 #include "flutter/fml/trace_event.h"
20 #include "flutter/fml/unique_fd.h"
21 
22 namespace fml {
23 
24 std::string CreateTemporaryDirectory() {
25  char directory_name[] = "/tmp/flutter_XXXXXXXX";
26  auto* result = ::mkdtemp(directory_name);
27  if (result == nullptr) {
28  return "";
29  }
30  return {result};
31 }
32 
33 static int ToPosixAccessFlags(FilePermission permission) {
34  int flags = 0;
35  switch (permission) {
37  flags |= O_RDONLY; // read only
38  break;
40  flags |= O_WRONLY; // write only
41  break;
43  flags |= O_RDWR; // read-write
44  break;
45  }
46  return flags;
47 }
48 
49 static int ToPosixCreateModeFlags(FilePermission permission) {
50  int mode = 0;
51  switch (permission) {
53  mode |= S_IRUSR;
54  break;
56  mode |= S_IWUSR;
57  break;
59  mode |= S_IRUSR | S_IWUSR;
60  break;
61  }
62  return mode;
63 }
64 
66  bool create_if_necessary,
67  FilePermission permission) {
68  return OpenFile(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
69  permission);
70 }
71 
72 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
73  const char* path,
74  bool create_if_necessary,
75  FilePermission permission) {
76  TRACE_EVENT0("flutter", "fml::OpenFile");
77  if (path == nullptr) {
78  return {};
79  }
80 
81  int flags = 0;
82  int mode = 0;
83 
84  if (create_if_necessary && !FileExists(base_directory, path)) {
85  flags = ToPosixAccessFlags(permission) | O_CREAT | O_TRUNC;
86  mode = ToPosixCreateModeFlags(permission);
87  } else {
88  flags = ToPosixAccessFlags(permission);
89  mode = 0; // Not creating since it already exists.
90  }
91 
92  return fml::UniqueFD{
93  FML_HANDLE_EINTR(::openat(base_directory.get(), path, flags, mode))};
94 }
95 
97  bool create_if_necessary,
98  FilePermission permission) {
99  return OpenDirectory(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
100  permission);
101 }
102 
104  const char* path,
105  bool create_if_necessary,
106  FilePermission permission) {
107  if (path == nullptr) {
108  return {};
109  }
110 
111  if (create_if_necessary && !FileExists(base_directory, path)) {
112  if (::mkdirat(base_directory.get(), path,
113  ToPosixCreateModeFlags(permission) | S_IXUSR) != 0) {
114  return {};
115  }
116  }
117 
119  ::openat(base_directory.get(), path, O_RDONLY | O_DIRECTORY))};
120 }
121 
123  return fml::UniqueFD{FML_HANDLE_EINTR(::dup(descriptor))};
124 }
125 
126 bool IsDirectory(const fml::UniqueFD& directory) {
127  if (!directory.is_valid()) {
128  return false;
129  }
130 
131  struct stat stat_result = {};
132 
133  if (::fstat(directory.get(), &stat_result) != 0) {
134  return false;
135  }
136 
137  return S_ISDIR(stat_result.st_mode);
138 }
139 
140 bool IsDirectory(const fml::UniqueFD& base_directory, const char* path) {
141  UniqueFD file = OpenFileReadOnly(base_directory, path);
142  return (file.is_valid() && IsDirectory(file));
143 }
144 
145 bool IsFile(const std::string& path) {
146  struct stat buf;
147  if (stat(path.c_str(), &buf) != 0) {
148  return false;
149  }
150 
151  return S_ISREG(buf.st_mode);
152 }
153 
154 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
155  if (!file.is_valid()) {
156  return false;
157  }
158 
159  return ::ftruncate(file.get(), size) == 0;
160 }
161 
162 bool UnlinkDirectory(const char* path) {
163  return UnlinkDirectory(fml::UniqueFD{AT_FDCWD}, path);
164 }
165 
166 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
167  return ::unlinkat(base_directory.get(), path, AT_REMOVEDIR) == 0;
168 }
169 
170 bool UnlinkFile(const char* path) {
171  return UnlinkFile(fml::UniqueFD{AT_FDCWD}, path);
172 }
173 
174 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
175  int code = ::unlinkat(base_directory.get(), path, 0);
176  if (code != 0) {
177  FML_DLOG(ERROR) << strerror(errno);
178  }
179  return code == 0;
180 }
181 
182 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
183  if (!base_directory.is_valid()) {
184  return false;
185  }
186 
187  return ::faccessat(base_directory.get(), path, F_OK, 0) == 0;
188 }
189 
190 bool WriteAtomically(const fml::UniqueFD& base_directory,
191  const char* file_name,
192  const Mapping& data) {
193  if (file_name == nullptr || data.GetMapping() == nullptr) {
194  return false;
195  }
196 
197  std::stringstream stream;
198  stream << file_name << ".temp";
199  const auto temp_file_name = stream.str();
200 
201  auto temp_file = OpenFile(base_directory, temp_file_name.c_str(), true,
203  if (!temp_file.is_valid()) {
204  return false;
205  }
206 
207  if (!TruncateFile(temp_file, data.GetSize())) {
208  return false;
209  }
210 
211  ssize_t remaining = data.GetSize();
212  ssize_t written = 0;
213  ssize_t offset = 0;
214 
215  while (remaining > 0) {
216  written = FML_HANDLE_EINTR(
217  ::write(temp_file.get(), data.GetMapping() + offset, remaining));
218 
219  if (written == -1) {
220  return false;
221  }
222 
223  remaining -= written;
224  offset += written;
225  }
226 
227  if (::fsync(temp_file.get()) != 0) {
228  return false;
229  }
230 
231  return ::renameat(base_directory.get(), temp_file_name.c_str(),
232  base_directory.get(), file_name) == 0;
233 }
234 
235 bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) {
236  fml::UniqueFD dup_fd(dup(directory.get()));
237  if (!dup_fd.is_valid()) {
238  FML_DLOG(ERROR) << "Can't dup the directory fd. Error: " << strerror(errno);
239  return true; // continue to visit other files
240  }
241 
242  fml::UniqueDir dir(::fdopendir(dup_fd.get()));
243  if (!dir.is_valid()) {
244  FML_DLOG(ERROR) << "Can't open the directory. Error: " << strerror(errno);
245  return true; // continue to visit other files
246  }
247 
248  // The directory fd will be closed by `closedir`.
249  (void)dup_fd.release();
250 
251  // Without `rewinddir`, `readir` will directly return NULL (end of dir is
252  // reached) after a previuos `VisitFiles` call for the same `const
253  // fml::UniqueFd& directory`.
254  rewinddir(dir.get());
255  while (dirent* ent = readdir(dir.get())) {
256  std::string filename = ent->d_name;
257  if (filename != "." && filename != "..") {
258  if (!visitor(directory, filename)) {
259  return false;
260  }
261  }
262  }
263 
264  return true;
265 }
266 
267 } // namespace fml
static int ToPosixCreateModeFlags(FilePermission permission)
Definition: file_posix.cc:49
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:24
virtual size_t GetSize() const =0
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition: file.h:98
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 observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable Disable the Dart Observatory The observatory is never available in release mode Bind to the IPv6 localhost address for the Dart Observatory Ignored if observatory 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:117
GAsyncResult * result
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:122
const T & get() const
Definition: unique_object.h:87
FlutterSemanticsFlag flags
Definition: ascii_trie.cc:9
bool is_valid() const
Definition: unique_object.h:89
fml::UniqueFD OpenDirectory(const char *path, bool create_if_necessary, FilePermission permission)
Definition: file_posix.cc:96
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:235
FilePermission
Definition: file.h:24
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition: switches.h:41
#define FML_HANDLE_EINTR(x)
Definition: eintr_wrapper.h:20
bool IsDirectory(const fml::UniqueFD &directory)
Definition: file_posix.cc:126
bool FileExists(const fml::UniqueFD &base_directory, const char *path)
Definition: file_posix.cc:182
bool UnlinkFile(const char *path)
Definition: file_posix.cc:170
bool IsFile(const std::string &path)
Definition: file_posix.cc:145
static int ToPosixAccessFlags(FilePermission permission)
Definition: file_posix.cc:33
fml::UniqueFD OpenFile(const char *path, bool create_if_necessary, FilePermission permission)
This can open a directory on POSIX, but not on Windows.
Definition: file_posix.cc:65
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
#define FML_DLOG(severity)
Definition: logging.h:85
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:154
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 observatory The hostname IP address on which the Dart Observatory should be served If not defaults to or::depending on whether ipv6 is specified disable Disable the Dart Observatory The observatory is never available in release mode Bind to the IPv6 localhost address for the Dart Observatory Ignored if observatory 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 Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent Remove all existing persistent cache This is mainly for debugging purposes such as reproducing the shader compilation jank use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all 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:194
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:190
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:162
fml::UniqueFD OpenFileReadOnly(const fml::UniqueFD &base_directory, const char *path)
Definition: file.cc:92