• 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 // ACE PC preivew.
8 #ifdef WINDOWS_PLATFORM
9 #include <fileapi.h>
10 #include <shlwapi.h>
11 #else
12 #include <Fileapi.h>
13 #include <Shlwapi.h>
14 #endif
15 
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <sys/stat.h>
19 
20 #include <algorithm>
21 #include <sstream>
22 
23 #include "flutter/fml/build_config.h"
24 #include "flutter/fml/mapping.h"
25 #include "flutter/fml/platform/win/errors_win.h"
26 #include "flutter/fml/platform/win/wstring_conversion.h"
27 
28 #if defined(OS_WIN)
29 #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
30 #endif
31 
32 namespace fml {
33 
GetFullHandlePath(const fml::UniqueFD & handle)34 static std::string GetFullHandlePath(const fml::UniqueFD& handle) {
35   wchar_t buffer[MAX_PATH] = {0};
36   const DWORD buffer_size = ::GetFinalPathNameByHandle(
37       handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED);
38   if (buffer_size == 0) {
39     FML_DLOG(ERROR) << "Could not get file handle path. "
40                     << GetLastErrorMessage();
41     return {};
42   }
43   return WideStringToString({buffer, buffer_size});
44 }
45 
GetAbsolutePath(const fml::UniqueFD & base_directory,const char * subpath)46 static std::string GetAbsolutePath(const fml::UniqueFD& base_directory,
47                                    const char* subpath) {
48   std::stringstream stream;
49   stream << GetFullHandlePath(base_directory) << "\\" << subpath;
50   auto path = stream.str();
51   std::replace(path.begin(), path.end(), '/', '\\');
52   return path;
53 }
54 
GetTemporaryDirectoryPath()55 static std::wstring GetTemporaryDirectoryPath() {
56   wchar_t wchar_path[MAX_PATH];
57   auto result_size = ::GetTempPath(MAX_PATH, wchar_path);
58   if (result_size > 0) {
59     return {wchar_path, result_size};
60   }
61   FML_DLOG(ERROR) << "Could not get temporary directory path. "
62                   << GetLastErrorMessage();
63   return {};
64 }
65 
GetDesiredAccessFlags(FilePermission permission)66 static DWORD GetDesiredAccessFlags(FilePermission permission) {
67   switch (permission) {
68     case FilePermission::kRead:
69       return GENERIC_READ;
70     case FilePermission::kWrite:
71       return GENERIC_WRITE;
72     case FilePermission::kReadWrite:
73       return GENERIC_READ | GENERIC_WRITE;
74   }
75   return GENERIC_READ;
76 }
77 
GetShareFlags(FilePermission permission)78 static DWORD GetShareFlags(FilePermission permission) {
79   switch (permission) {
80     case FilePermission::kRead:
81       return FILE_SHARE_READ;
82     case FilePermission::kWrite:
83       return FILE_SHARE_WRITE;
84     case FilePermission::kReadWrite:
85       return FILE_SHARE_READ | FILE_SHARE_WRITE;
86   }
87   return FILE_SHARE_READ;
88 }
89 
CreateTemporaryDirectory()90 std::string CreateTemporaryDirectory() {
91   // Get the system temporary directory.
92   auto temp_dir_container = GetTemporaryDirectoryPath();
93   if (temp_dir_container.size() == 0) {
94     FML_DLOG(ERROR) << "Could not get system temporary directory.";
95     return {};
96   }
97 
98   // Create a UUID.
99   UUID uuid;
100   RPC_STATUS status = UuidCreateSequential(&uuid);
101   if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) {
102     FML_DLOG(ERROR) << "Could not create UUID";
103     return {};
104   }
105 
106   RPC_WSTR uuid_string;
107   status = UuidToString(&uuid, &uuid_string);
108   if (status != RPC_S_OK) {
109     FML_DLOG(ERROR) << "Could not create UUID to string.";
110     return {};
111   }
112 
113   std::wstring uuid_str(reinterpret_cast<wchar_t*>(uuid_string));
114   RpcStringFree(&uuid_string);
115 
116   // Join the two and create a path to the new temporary directory.
117 
118   std::wstringstream stream;
119   stream << temp_dir_container << "\\" << uuid_str;
120   auto temp_dir = stream.str();
121 
122   auto dir_fd = OpenDirectory(WideStringToString(temp_dir).c_str(), true,
123                               FilePermission::kReadWrite);
124   if (!dir_fd.is_valid()) {
125     FML_DLOG(ERROR) << "Could not get temporary directory FD. "
126                     << GetLastErrorMessage();
127     return {};
128   }
129 
130   return WideStringToString(std::move(temp_dir));
131 }
132 
OpenFile(const fml::UniqueFD & base_directory,const char * path,bool create_if_necessary,FilePermission permission)133 fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory,
134                        const char* path,
135                        bool create_if_necessary,
136                        FilePermission permission) {
137   return OpenFile(GetAbsolutePath(base_directory, path).c_str(),
138                   create_if_necessary, permission);
139 }
140 
OpenFile(const char * path,bool create_if_necessary,FilePermission permission)141 fml::UniqueFD OpenFile(const char* path,
142                        bool create_if_necessary,
143                        FilePermission permission) {
144   if (path == nullptr || strlen(path) == 0) {
145     return {};
146   }
147 
148   auto file_name = StringToWideString({path});
149 
150   if (file_name.size() == 0) {
151     return {};
152   }
153 
154   const DWORD creation_disposition =
155       create_if_necessary ? CREATE_NEW : OPEN_EXISTING;
156 
157   const DWORD flags = FILE_ATTRIBUTE_NORMAL;
158 
159   auto handle =
160       CreateFile(file_name.c_str(),                  // lpFileName
161                  GetDesiredAccessFlags(permission),  // dwDesiredAccess
162                  GetShareFlags(permission),          // dwShareMode
163                  nullptr,                            // lpSecurityAttributes  //
164                  creation_disposition,               // dwCreationDisposition //
165                  flags,   // dwFlagsAndAttributes                  //
166                  nullptr  // hTemplateFile                         //
167       );
168 
169   if (handle == INVALID_HANDLE_VALUE) {
170     FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
171     return {};
172   }
173 
174   return fml::UniqueFD{handle};
175 }
176 
OpenDirectory(const fml::UniqueFD & base_directory,const char * path,bool create_if_necessary,FilePermission permission)177 fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
178                             const char* path,
179                             bool create_if_necessary,
180                             FilePermission permission) {
181   return OpenDirectory(GetAbsolutePath(base_directory, path).c_str(),
182                        create_if_necessary, permission);
183 }
184 
OpenDirectory(const char * path,bool create_if_necessary,FilePermission permission)185 fml::UniqueFD OpenDirectory(const char* path,
186                             bool create_if_necessary,
187                             FilePermission permission) {
188   if (path == nullptr || strlen(path) == 0) {
189     return {};
190   }
191 
192   auto file_name = StringToWideString({path});
193 
194   if (file_name.size() == 0) {
195     return {};
196   }
197 
198   if (create_if_necessary) {
199     if (!::CreateDirectory(file_name.c_str(), nullptr)) {
200       if (GetLastError() != ERROR_ALREADY_EXISTS) {
201         FML_DLOG(ERROR) << "Could not create directory. "
202                         << GetLastErrorMessage();
203         return {};
204       }
205     }
206   }
207 
208   const DWORD creation_disposition = OPEN_EXISTING;
209 
210   const DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
211 
212   auto handle =
213       CreateFile(file_name.c_str(),                  // lpFileName
214                  GetDesiredAccessFlags(permission),  // dwDesiredAccess
215                  GetShareFlags(permission),          // dwShareMode
216                  nullptr,                            // lpSecurityAttributes  //
217                  creation_disposition,               // dwCreationDisposition //
218                  flags,   // dwFlagsAndAttributes                  //
219                  nullptr  // hTemplateFile                         //
220       );
221 
222   if (handle == INVALID_HANDLE_VALUE) {
223     FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage();
224     return {};
225   }
226 
227   return fml::UniqueFD{handle};
228 }
229 
Duplicate(fml::UniqueFD::element_type descriptor)230 fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) {
231   if (descriptor == INVALID_HANDLE_VALUE) {
232     return fml::UniqueFD{};
233   }
234 
235   HANDLE duplicated = INVALID_HANDLE_VALUE;
236 
237   if (!::DuplicateHandle(
238           GetCurrentProcess(),  // source process
239           descriptor,           // source handle
240           GetCurrentProcess(),  // target process
241           &duplicated,          // target handle
242           0,      // desired access (ignored because DUPLICATE_SAME_ACCESS)
243           FALSE,  // inheritable
244           DUPLICATE_SAME_ACCESS)  // options
245   ) {
246     return fml::UniqueFD{};
247   }
248 
249   return fml::UniqueFD{duplicated};
250 }
251 
IsDirectory(const fml::UniqueFD & directory)252 bool IsDirectory(const fml::UniqueFD& directory) {
253   BY_HANDLE_FILE_INFORMATION info;
254   if (!::GetFileInformationByHandle(directory.get(), &info)) {
255     FML_DLOG(ERROR) << "Could not get file information. "
256                     << GetLastErrorMessage();
257     return false;
258   }
259   return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
260 }
261 
IsFile(const std::string & path)262 bool IsFile(const std::string& path) {
263   struct stat buf;
264   if (stat(path.c_str(), &buf) != 0)
265     return false;
266   return S_ISREG(buf.st_mode);
267 }
268 
UnlinkDirectory(const char * path)269 bool UnlinkDirectory(const char* path) {
270   if (!::RemoveDirectory(ConvertToWString(path).c_str())) {
271     FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
272                     << GetLastErrorMessage();
273     return false;
274   }
275   return true;
276 }
277 
UnlinkDirectory(const fml::UniqueFD & base_directory,const char * path)278 bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) {
279   if (!::RemoveDirectory(
280           StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
281     FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. "
282                     << GetLastErrorMessage();
283     return false;
284   }
285   return true;
286 }
287 
UnlinkFile(const char * path)288 bool UnlinkFile(const char* path) {
289   if (!::DeleteFile(ConvertToWString(path).c_str())) {
290     FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
291                     << GetLastErrorMessage();
292     return false;
293   }
294   return true;
295 }
296 
UnlinkFile(const fml::UniqueFD & base_directory,const char * path)297 bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) {
298   if (!::DeleteFile(
299           StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) {
300     FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. "
301                     << GetLastErrorMessage();
302     return false;
303   }
304   return true;
305 }
306 
TruncateFile(const fml::UniqueFD & file,size_t size)307 bool TruncateFile(const fml::UniqueFD& file, size_t size) {
308   LARGE_INTEGER large_size;
309   large_size.QuadPart = size;
310   large_size.LowPart = SetFilePointer(file.get(), large_size.LowPart,
311                                       &large_size.HighPart, FILE_BEGIN);
312   if (large_size.LowPart == INVALID_SET_FILE_POINTER &&
313       GetLastError() != NO_ERROR) {
314     FML_DLOG(ERROR) << "Could not update file size. " << GetLastErrorMessage();
315     return false;
316   }
317 
318   if (!::SetEndOfFile(file.get())) {
319     FML_DLOG(ERROR) << "Could not commit file size update. "
320                     << GetLastErrorMessage();
321     return false;
322   }
323   return true;
324 }
325 
FileExists(const fml::UniqueFD & base_directory,const char * path)326 bool FileExists(const fml::UniqueFD& base_directory, const char* path) {
327   return IsFile(GetAbsolutePath(base_directory, path).c_str());
328 }
329 
WriteAtomically(const fml::UniqueFD & base_directory,const char * file_name,const Mapping & mapping)330 bool WriteAtomically(const fml::UniqueFD& base_directory,
331                      const char* file_name,
332                      const Mapping& mapping) {
333   if (file_name == nullptr) {
334     return false;
335   }
336 
337   auto file_path = GetAbsolutePath(base_directory, file_name);
338   std::stringstream stream;
339   stream << file_path << ".temp";
340   auto temp_file_path = stream.str();
341 
342   auto temp_file =
343       OpenFile(temp_file_path.c_str(), true, FilePermission::kReadWrite);
344 
345   if (!temp_file.is_valid()) {
346     FML_DLOG(ERROR) << "Could not create temporary file.";
347     return false;
348   }
349 
350   if (!TruncateFile(temp_file, mapping.GetSize())) {
351     FML_DLOG(ERROR) << "Could not truncate the file to the correct size. "
352                     << GetLastErrorMessage();
353     return false;
354   }
355 
356   {
357     FileMapping temp_file_mapping(temp_file, {FileMapping::Protection::kRead,
358                                               FileMapping::Protection::kWrite});
359     if (temp_file_mapping.GetSize() != mapping.GetSize()) {
360       FML_DLOG(ERROR) << "Temporary file mapping size was incorrect. Is "
361                       << temp_file_mapping.GetSize() << ". Should be "
362                       << mapping.GetSize() << ".";
363       return false;
364     }
365 
366     if (temp_file_mapping.GetMutableMapping() == nullptr) {
367       FML_DLOG(ERROR) << "Temporary file does not have a mutable mapping.";
368       return false;
369     }
370 
371     ::memcpy(temp_file_mapping.GetMutableMapping(), mapping.GetMapping(),
372              mapping.GetSize());
373 
374     if (!::FlushViewOfFile(temp_file_mapping.GetMutableMapping(),
375                            mapping.GetSize())) {
376       FML_DLOG(ERROR) << "Could not flush file view. " << GetLastErrorMessage();
377       return false;
378     }
379 
380     if (!::FlushFileBuffers(temp_file.get())) {
381       FML_DLOG(ERROR) << "Could not flush file buffers. "
382                       << GetLastErrorMessage();
383       return false;
384     }
385 
386     // File mapping is detroyed.
387   }
388 
389   temp_file.reset();
390 
391   if (!::MoveFile(StringToWideString(temp_file_path).c_str(),
392                   StringToWideString(file_path).c_str())) {
393     FML_DLOG(ERROR)
394         << "Could not replace temp file at correct path. File path: "
395         << file_path << ". Temp file path: " << temp_file_path << " "
396         << GetLastErrorMessage();
397     return false;
398   }
399 
400   return true;
401 }
402 
403 }  // namespace fml
404