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