• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fcntl.h>
8 #include <sys/mman.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <cstring>
12 
13 #include <memory>
14 #include <sstream>
15 
16 #include "flutter/fml/eintr_wrapper.h"
17 #include "flutter/fml/mapping.h"
18 
19 namespace fml {
20 
CreateTemporaryDirectory()21 std::string CreateTemporaryDirectory() {
22   char directory_name[] = "/tmp/flutter_XXXXXXXX";
23   auto* result = ::mkdtemp(directory_name);
24   if (result == nullptr) {
25     return "";
26   }
27   return {result};
28 }
29 
ToPosixAccessFlags(FilePermission permission)30 static int ToPosixAccessFlags(FilePermission permission) {
31   int flags = 0;
32   switch (permission) {
33     case FilePermission::kRead:
34       flags |= O_RDONLY;  // read only
35       break;
36     case FilePermission::kWrite:
37       flags |= O_WRONLY;  // write only
38       break;
39     case FilePermission::kReadWrite:
40       flags |= O_RDWR;  // read-write
41       break;
42   }
43   return flags;
44 }
45 
ToPosixCreateModeFlags(FilePermission permission)46 static int ToPosixCreateModeFlags(FilePermission permission) {
47   int mode = 0;
48   switch (permission) {
49     case FilePermission::kRead:
50       mode |= S_IRUSR;
51       break;
52     case FilePermission::kWrite:
53       mode |= S_IWUSR;
54       break;
55     case FilePermission::kReadWrite:
56       mode |= S_IRUSR | S_IWUSR;
57       break;
58   }
59   return mode;
60 }
61 
OpenFile(const char * path,bool create_if_necessary,FilePermission permission)62 fml::UniqueFD OpenFile(const char* path,
63                        bool create_if_necessary,
64                        FilePermission permission) {
65   return OpenFile(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
66                   permission);
67 }
68 
OpenFile(const fml::UniqueFD & base_directory,const char * path,bool create_if_necessary,FilePermission permission)69 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
70                        const char* path,
71                        bool create_if_necessary,
72                        FilePermission permission) {
73   if (path == nullptr) {
74     return {};
75   }
76 
77   int flags = 0;
78   int mode = 0;
79 
80   if (create_if_necessary && !FileExists(base_directory, path)) {
81     flags = ToPosixAccessFlags(permission) | O_CREAT | O_TRUNC;
82     mode = ToPosixCreateModeFlags(permission);
83   } else {
84     flags = ToPosixAccessFlags(permission);
85     mode = 0;  // Not creating since it already exists.
86   }
87 
88   return fml::UniqueFD{
89       FML_HANDLE_EINTR(::openat(base_directory.get(), path, flags, mode))};
90 }
91 
OpenDirectory(const char * path,bool create_if_necessary,FilePermission permission)92 fml::UniqueFD OpenDirectory(const char* path,
93                             bool create_if_necessary,
94                             FilePermission permission) {
95   return OpenDirectory(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary,
96                        permission);
97 }
98 
OpenDirectory(const fml::UniqueFD & base_directory,const char * path,bool create_if_necessary,FilePermission permission)99 fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
100                             const char* path,
101                             bool create_if_necessary,
102                             FilePermission permission) {
103   if (path == nullptr) {
104     return {};
105   }
106 
107   if (create_if_necessary && !FileExists(base_directory, path)) {
108     if (::mkdirat(base_directory.get(), path,
109                   ToPosixCreateModeFlags(permission) | S_IXUSR) != 0) {
110       return {};
111     }
112   }
113 
114   return fml::UniqueFD{FML_HANDLE_EINTR(
115       ::openat(base_directory.get(), path, O_RDONLY | O_DIRECTORY))};
116 }
117 
Duplicate(fml::UniqueFD::element_type descriptor)118 fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) {
119   return fml::UniqueFD{FML_HANDLE_EINTR(::dup(descriptor))};
120 }
121 
IsDirectory(const fml::UniqueFD & directory)122 bool IsDirectory(const fml::UniqueFD& directory) {
123   if (!directory.is_valid()) {
124     return false;
125   }
126 
127   struct stat stat_result = {};
128 
129   if (::fstat(directory.get(), &stat_result) != 0) {
130     return false;
131   }
132 
133   return S_ISDIR(stat_result.st_mode);
134 }
135 
IsFile(const std::string & path)136 bool IsFile(const std::string& path) {
137   struct stat buf;
138   if (stat(path.c_str(), &buf) != 0) {
139     return false;
140   }
141 
142   return S_ISREG(buf.st_mode);
143 }
144 
TruncateFile(const fml::UniqueFD & file,size_t size)145 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
146   if (!file.is_valid()) {
147     return false;
148   }
149 
150   return ::ftruncate(file.get(), size) == 0;
151 }
152 
UnlinkDirectory(const char * path)153 bool UnlinkDirectory(const char* path) {
154   return UnlinkDirectory(fml::UniqueFD{AT_FDCWD}, path);
155 }
156 
UnlinkDirectory(const fml::UniqueFD & base_directory,const char * path)157 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
158   return ::unlinkat(base_directory.get(), path, AT_REMOVEDIR) == 0;
159 }
160 
UnlinkFile(const char * path)161 bool UnlinkFile(const char* path) {
162   return UnlinkFile(fml::UniqueFD{AT_FDCWD}, path);
163 }
164 
UnlinkFile(const fml::UniqueFD & base_directory,const char * path)165 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
166   return ::unlinkat(base_directory.get(), path, 0) == 0;
167 }
168 
FileExists(const fml::UniqueFD & base_directory,const char * path)169 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
170   if (!base_directory.is_valid()) {
171     return false;
172   }
173 
174   return ::faccessat(base_directory.get(), path, F_OK, 0) == 0;
175 }
176 
WriteAtomically(const fml::UniqueFD & base_directory,const char * file_name,const Mapping & data)177 bool WriteAtomically(const fml::UniqueFD& base_directory,
178                      const char* file_name,
179                      const Mapping& data) {
180   if (file_name == nullptr || data.GetMapping() == nullptr) {
181     return false;
182   }
183 
184   std::stringstream stream;
185   stream << file_name << ".temp";
186   const auto temp_file_name = stream.str();
187 
188   auto temp_file = OpenFile(base_directory, temp_file_name.c_str(), true,
189                             FilePermission::kReadWrite);
190   if (!temp_file.is_valid()) {
191     return false;
192   }
193 
194   if (!TruncateFile(temp_file, data.GetSize())) {
195     return false;
196   }
197 
198   FileMapping mapping(temp_file, {FileMapping::Protection::kWrite});
199   if (mapping.GetMutableMapping() == nullptr ||
200       data.GetSize() != mapping.GetSize()) {
201     return false;
202   }
203 
204   ::memcpy(mapping.GetMutableMapping(), data.GetMapping(), data.GetSize());
205 
206   if (::msync(mapping.GetMutableMapping(), data.GetSize(), MS_SYNC) != 0) {
207     return false;
208   }
209 
210   return ::renameat(base_directory.get(), temp_file_name.c_str(),
211                     base_directory.get(), file_name) == 0;
212 }
213 
214 }  // namespace fml
215