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/unique_fd.h"
20 
21 namespace fml {
22 
23 std::string CreateTemporaryDirectory() {
24  char directory_name[] = "/tmp/flutter_XXXXXXXX";
25  auto* result = ::mkdtemp(directory_name);
26  if (result == nullptr) {
27  return "";
28  }
29  return {result};
30 }
31 
32 static int ToPosixAccessFlags(FilePermission permission) {
33  int flags = 0;
34  switch (permission) {
36  flags |= O_RDONLY; // read only
37  break;
39  flags |= O_WRONLY; // write only
40  break;
42  flags |= O_RDWR; // read-write
43  break;
44  }
45  return flags;
46 }
47 
48 static int ToPosixCreateModeFlags(FilePermission permission) {
49  int mode = 0;
50  switch (permission) {
52  mode |= S_IRUSR;
53  break;
55  mode |= S_IWUSR;
56  break;
58  mode |= S_IRUSR | S_IWUSR;
59  break;
60  }
61  return mode;
62 }
63 
65  bool create_if_necessary,
66  FilePermission permission) {
67  return OpenFile(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
68  permission);
69 }
70 
71 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
72  const char* path,
73  bool create_if_necessary,
74  FilePermission permission) {
75  if (path == nullptr) {
76  return {};
77  }
78 
79  int flags = 0;
80  int mode = 0;
81 
82  if (create_if_necessary && !FileExists(base_directory, path)) {
83  flags = ToPosixAccessFlags(permission) | O_CREAT | O_TRUNC;
84  mode = ToPosixCreateModeFlags(permission);
85  } else {
86  flags = ToPosixAccessFlags(permission);
87  mode = 0; // Not creating since it already exists.
88  }
89 
90  return fml::UniqueFD{
91  FML_HANDLE_EINTR(::openat(base_directory.get(), path, flags, mode))};
92 }
93 
95  bool create_if_necessary,
96  FilePermission permission) {
97  return OpenDirectory(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
98  permission);
99 }
100 
102  const char* path,
103  bool create_if_necessary,
104  FilePermission permission) {
105  if (path == nullptr) {
106  return {};
107  }
108 
109  if (create_if_necessary && !FileExists(base_directory, path)) {
110  if (::mkdirat(base_directory.get(), path,
111  ToPosixCreateModeFlags(permission) | S_IXUSR) != 0) {
112  return {};
113  }
114  }
115 
117  ::openat(base_directory.get(), path, O_RDONLY | O_DIRECTORY))};
118 }
119 
121  return fml::UniqueFD{FML_HANDLE_EINTR(::dup(descriptor))};
122 }
123 
124 bool IsDirectory(const fml::UniqueFD& directory) {
125  if (!directory.is_valid()) {
126  return false;
127  }
128 
129  struct stat stat_result = {};
130 
131  if (::fstat(directory.get(), &stat_result) != 0) {
132  return false;
133  }
134 
135  return S_ISDIR(stat_result.st_mode);
136 }
137 
138 bool IsDirectory(const fml::UniqueFD& base_directory, const char* path) {
139  UniqueFD file = OpenFileReadOnly(base_directory, path);
140  return (file.is_valid() && IsDirectory(file));
141 }
142 
143 bool IsFile(const std::string& path) {
144  struct stat buf;
145  if (stat(path.c_str(), &buf) != 0) {
146  return false;
147  }
148 
149  return S_ISREG(buf.st_mode);
150 }
151 
152 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
153  if (!file.is_valid()) {
154  return false;
155  }
156 
157  return ::ftruncate(file.get(), size) == 0;
158 }
159 
160 bool UnlinkDirectory(const char* path) {
161  return UnlinkDirectory(fml::UniqueFD{AT_FDCWD}, path);
162 }
163 
164 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
165  return ::unlinkat(base_directory.get(), path, AT_REMOVEDIR) == 0;
166 }
167 
168 bool UnlinkFile(const char* path) {
169  return UnlinkFile(fml::UniqueFD{AT_FDCWD}, path);
170 }
171 
172 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
173  int code = ::unlinkat(base_directory.get(), path, 0);
174  if (code != 0) {
175  FML_DLOG(ERROR) << strerror(errno);
176  }
177  return code == 0;
178 }
179 
180 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
181  if (!base_directory.is_valid()) {
182  return false;
183  }
184 
185  return ::faccessat(base_directory.get(), path, F_OK, 0) == 0;
186 }
187 
188 bool WriteAtomically(const fml::UniqueFD& base_directory,
189  const char* file_name,
190  const Mapping& data) {
191  if (file_name == nullptr || data.GetMapping() == nullptr) {
192  return false;
193  }
194 
195  std::stringstream stream;
196  stream << file_name << ".temp";
197  const auto temp_file_name = stream.str();
198 
199  auto temp_file = OpenFile(base_directory, temp_file_name.c_str(), true,
201  if (!temp_file.is_valid()) {
202  return false;
203  }
204 
205  if (!TruncateFile(temp_file, data.GetSize())) {
206  return false;
207  }
208 
209  ssize_t remaining = data.GetSize();
210  ssize_t written = 0;
211  ssize_t offset = 0;
212 
213  while (remaining > 0) {
214  written = FML_HANDLE_EINTR(
215  ::write(temp_file.get(), data.GetMapping() + offset, remaining));
216 
217  if (written == -1) {
218  return false;
219  }
220 
221  remaining -= written;
222  offset += written;
223  }
224 
225  if (::fsync(temp_file.get()) != 0) {
226  return false;
227  }
228 
229  return ::renameat(base_directory.get(), temp_file_name.c_str(),
230  base_directory.get(), file_name) == 0;
231 }
232 
233 bool VisitFiles(const fml::UniqueFD& directory, const FileVisitor& visitor) {
234  fml::UniqueFD dup_fd(dup(directory.get()));
235  if (!dup_fd.is_valid()) {
236  FML_DLOG(ERROR) << "Can't dup the directory fd. Error: " << strerror(errno);
237  return true; // continue to visit other files
238  }
239 
240  fml::UniqueDir dir(::fdopendir(dup_fd.get()));
241  if (!dir.is_valid()) {
242  FML_DLOG(ERROR) << "Can't open the directory. Error: " << strerror(errno);
243  return true; // continue to visit other files
244  }
245 
246  // The directory fd will be closed by `closedir`.
247  (void)dup_fd.release();
248 
249  // Without `rewinddir`, `readir` will directly return NULL (end of dir is
250  // reached) after a previuos `VisitFiles` call for the same `const
251  // fml::UniqueFd& directory`.
252  rewinddir(dir.get());
253  while (dirent* ent = readdir(dir.get())) {
254  std::string filename = ent->d_name;
255  if (filename != "." && filename != "..") {
256  if (!visitor(directory, filename)) {
257  return false;
258  }
259  }
260  }
261 
262  return true;
263 }
264 
265 } // namespace fml
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
static int ToPosixCreateModeFlags(FilePermission permission)
Definition: file_posix.cc:48
std::string CreateTemporaryDirectory()
Definition: file_posix.cc:23
virtual size_t GetSize() const =0
std::function< bool(const fml::UniqueFD &directory, const std::string &filename)> FileVisitor
Definition: file.h:98
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:120
const T & get() const
Definition: unique_object.h:87
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:94
bool VisitFiles(const fml::UniqueFD &directory, const FileVisitor &visitor)
Definition: file_posix.cc:233
FilePermission
Definition: file.h:24
#define FML_HANDLE_EINTR(x)
Definition: eintr_wrapper.h:33
bool IsDirectory(const fml::UniqueFD &directory)
Definition: file_posix.cc:124
bool FileExists(const fml::UniqueFD &base_directory, const char *path)
Definition: file_posix.cc:180
bool UnlinkFile(const char *path)
Definition: file_posix.cc:168
bool IsFile(const std::string &path)
Definition: file_posix.cc:143
static int ToPosixAccessFlags(FilePermission permission)
Definition: file_posix.cc:32
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:64
#define FML_DLOG(severity)
Definition: logging.h:85
bool TruncateFile(const fml::UniqueFD &file, size_t size)
Definition: file_posix.cc:152
bool WriteAtomically(const fml::UniqueFD &base_directory, const char *file_name, const Mapping &mapping)
Definition: file_posix.cc:188
bool UnlinkDirectory(const char *path)
Definition: file_posix.cc:160
fml::UniqueFD OpenFileReadOnly(const fml::UniqueFD &base_directory, const char *path)
Definition: file.cc:92
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol Prefix for the symbols representing ICU data linked into the Flutter library dart flags
Definition: switches.h:66