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
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
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
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
#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
#define FML_DLOG(severity)
Definition: logging.h:85
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:154
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