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 "flutter/fml/logging.h" |
8 | #include "flutter/fml/unique_fd.h" |
9 | |
10 | namespace fml { |
11 | |
12 | static fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, |
13 | const std::vector<std::string>& components, |
14 | FilePermission permission, |
15 | size_t index) { |
16 | FML_DCHECK(index <= components.size()); |
17 | |
18 | const char* file_path = components[index].c_str(); |
19 | |
20 | auto directory = OpenDirectory(base_directory, file_path, true, permission); |
21 | |
22 | if (!directory.is_valid()) { |
23 | return {}; |
24 | } |
25 | |
26 | if (index == components.size() - 1) { |
27 | return directory; |
28 | } |
29 | |
30 | return CreateDirectory(directory, components, permission, index + 1); |
31 | } |
32 | |
33 | fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, |
34 | const std::vector<std::string>& components, |
35 | FilePermission permission) { |
36 | if (!IsDirectory(base_directory)) { |
37 | return {}; |
38 | } |
39 | |
40 | if (components.size() == 0) { |
41 | return {}; |
42 | } |
43 | |
44 | return CreateDirectory(base_directory, components, permission, 0); |
45 | } |
46 | |
47 | ScopedTemporaryDirectory::ScopedTemporaryDirectory() |
48 | : path_(CreateTemporaryDirectory()) { |
49 | if (path_ != "" ) { |
50 | dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead); |
51 | } |
52 | } |
53 | |
54 | ScopedTemporaryDirectory::~ScopedTemporaryDirectory() { |
55 | // POSIX requires the directory to be empty before UnlinkDirectory. |
56 | if (path_ != "" ) { |
57 | if (!RemoveFilesInDirectory(dir_fd_)) { |
58 | FML_LOG(ERROR) << "Could not clean directory: " << path_; |
59 | } |
60 | } |
61 | |
62 | // Windows has to close UniqueFD first before UnlinkDirectory |
63 | dir_fd_.reset(); |
64 | if (path_ != "" ) { |
65 | if (!UnlinkDirectory(path_.c_str())) { |
66 | FML_LOG(ERROR) << "Could not remove directory: " << path_; |
67 | } |
68 | } |
69 | } |
70 | |
71 | bool VisitFilesRecursively(const fml::UniqueFD& directory, |
72 | const FileVisitor& visitor) { |
73 | FileVisitor recursive_visitor = [&recursive_visitor, &visitor]( |
74 | const UniqueFD& directory, |
75 | const std::string& filename) { |
76 | if (!visitor(directory, filename)) { |
77 | return false; |
78 | } |
79 | if (IsDirectory(directory, filename.c_str())) { |
80 | UniqueFD sub_dir = OpenDirectoryReadOnly(directory, filename.c_str()); |
81 | if (!sub_dir.is_valid()) { |
82 | FML_LOG(ERROR) << "Can't open sub-directory: " << filename; |
83 | return true; |
84 | } |
85 | return VisitFiles(sub_dir, recursive_visitor); |
86 | } |
87 | return true; |
88 | }; |
89 | return VisitFiles(directory, recursive_visitor); |
90 | } |
91 | |
92 | fml::UniqueFD OpenFileReadOnly(const fml::UniqueFD& base_directory, |
93 | const char* path) { |
94 | return OpenFile(base_directory, path, false, FilePermission::kRead); |
95 | } |
96 | |
97 | fml::UniqueFD OpenDirectoryReadOnly(const fml::UniqueFD& base_directory, |
98 | const char* path) { |
99 | return OpenDirectory(base_directory, path, false, FilePermission::kRead); |
100 | } |
101 | |
102 | bool RemoveFilesInDirectory(const fml::UniqueFD& directory) { |
103 | fml::FileVisitor recursive_cleanup = [&recursive_cleanup]( |
104 | const fml::UniqueFD& directory, |
105 | const std::string& filename) { |
106 | bool removed; |
107 | if (fml::IsDirectory(directory, filename.c_str())) { |
108 | fml::UniqueFD sub_dir = |
109 | OpenDirectoryReadOnly(directory, filename.c_str()); |
110 | removed = VisitFiles(sub_dir, recursive_cleanup) && |
111 | fml::UnlinkDirectory(directory, filename.c_str()); |
112 | } else { |
113 | removed = fml::UnlinkFile(directory, filename.c_str()); |
114 | } |
115 | return removed; |
116 | }; |
117 | return VisitFiles(directory, recursive_cleanup); |
118 | } |
119 | |
120 | bool RemoveDirectoryRecursively(const fml::UniqueFD& parent, |
121 | const char* directory_name) { |
122 | auto dir = fml::OpenDirectory(parent, directory_name, false, |
123 | fml::FilePermission::kReadWrite); |
124 | return RemoveFilesInDirectory(dir) && UnlinkDirectory(parent, directory_name); |
125 | } |
126 | |
127 | } // namespace fml |
128 | |