1 /*
2 * Copyright (C) 2015, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "io_delegate.h"
18
19 #include <cstring>
20 #include <fstream>
21 #include <type_traits>
22 #include <vector>
23
24 #ifdef _WIN32
25 #include <direct.h>
26 #include <windows.h>
27 #undef ERROR
28 #else
29 #include <dirent.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #endif
34
35 #include <android-base/strings.h>
36
37 #include "logging.h"
38 #include "os.h"
39
40 using std::string;
41 using std::unique_ptr;
42 using std::vector;
43
44 using android::base::Error;
45 using android::base::Result;
46 using android::base::Split;
47 using android::base::StartsWith;
48
49 namespace android {
50 namespace aidl {
51
GetAbsolutePath(const string & path,string * absolute_path)52 bool IoDelegate::GetAbsolutePath(const string& path, string* absolute_path) {
53 #ifdef _WIN32
54
55 char buf[4096];
56 DWORD path_len = GetFullPathName(path.c_str(), sizeof(buf), buf, nullptr);
57 if (path_len <= 0 || path_len >= sizeof(buf)) {
58 AIDL_ERROR(path) << "Failed to GetFullPathName";
59 return false;
60 }
61 *absolute_path = buf;
62
63 return true;
64
65 #else
66
67 if (path.empty()) {
68 AIDL_ERROR(path) << "Giving up on finding an absolute path to represent the empty string.";
69 return false;
70 }
71 if (path[0] == OS_PATH_SEPARATOR) {
72 *absolute_path = path;
73 return true;
74 }
75
76 char buf[4096];
77 if (getcwd(buf, sizeof(buf)) == nullptr) {
78 AIDL_ERROR(path) << "Path of current working directory does not fit in " << sizeof(buf)
79 << " bytes";
80 return false;
81 }
82
83 *absolute_path = buf;
84 *absolute_path += OS_PATH_SEPARATOR;
85 *absolute_path += path;
86 return true;
87 #endif
88 }
89
GetFileContents(const string & filename,const string & content_suffix) const90 unique_ptr<string> IoDelegate::GetFileContents(
91 const string& filename,
92 const string& content_suffix) const {
93 unique_ptr<string> contents;
94 std::ifstream in(filename, std::ios::in | std::ios::binary);
95 if (!in) {
96 return contents;
97 }
98 contents.reset(new string);
99 in.seekg(0, std::ios::end);
100 ssize_t file_size = in.tellg();
101 contents->resize(file_size + content_suffix.length());
102 in.seekg(0, std::ios::beg);
103 // Read the file contents into the beginning of the string
104 in.read(&(*contents)[0], file_size);
105 // Drop the suffix in at the end.
106 contents->replace(file_size, content_suffix.length(), content_suffix);
107 in.close();
108
109 return contents;
110 }
111
FileIsReadable(const string & path) const112 bool IoDelegate::FileIsReadable(const string& path) const {
113 #ifdef _WIN32
114 // check that the file exists and is not write-only
115 return (0 == _access(path.c_str(), 0)) && // mode 0=exist
116 (0 == _access(path.c_str(), 4)); // mode 4=readable
117 #else
118 return (0 == access(path.c_str(), R_OK));
119 #endif
120 }
121
CreateNestedDirs(const string & caller_base_dir,const vector<string> & nested_subdirs)122 static bool CreateNestedDirs(const string& caller_base_dir, const vector<string>& nested_subdirs) {
123 string base_dir = caller_base_dir;
124 if (base_dir.empty()) {
125 base_dir = ".";
126 }
127 for (const string& subdir : nested_subdirs) {
128 if (base_dir[base_dir.size() - 1] != OS_PATH_SEPARATOR) {
129 base_dir += OS_PATH_SEPARATOR;
130 }
131 base_dir += subdir;
132 bool success;
133 #ifdef _WIN32
134 success = _mkdir(base_dir.c_str()) == 0;
135 #else
136 success = mkdir(base_dir.c_str(),
137 S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
138 #endif
139 // On darwin when you try to mkdir("/", ...) we get EISDIR.
140 if (!success && (errno != EEXIST && errno != EISDIR)) {
141 AIDL_ERROR(caller_base_dir) << "Error while creating " << base_dir << ": " << strerror(errno);
142 return false;
143 }
144 }
145 return true;
146 }
147
CreateDirForPath(const string & path) const148 bool IoDelegate::CreateDirForPath(const string& path) const {
149 if (path.empty()) {
150 return true;
151 }
152
153 string absolute_path;
154 if (!GetAbsolutePath(path, &absolute_path)) {
155 return false;
156 }
157
158 auto directories = Split(absolute_path, string{OS_PATH_SEPARATOR});
159
160 // The "base" directory is just the root of the file system. On Windows,
161 // this will look like "C:\" but on Unix style file systems we get an empty
162 // string after splitting "/foo" with "/"
163 string base = directories[0];
164 if (base.empty()) {
165 base = "/";
166 }
167 directories.erase(directories.begin());
168
169 // Remove the actual file in question, we're just creating the directory path.
170 bool is_file = path.back() != OS_PATH_SEPARATOR;
171 if (is_file) {
172 directories.pop_back();
173 }
174
175 return CreateNestedDirs(base, directories);
176 }
177
GetCodeWriter(const string & file_path) const178 unique_ptr<CodeWriter> IoDelegate::GetCodeWriter(
179 const string& file_path) const {
180 if (CreateDirForPath(file_path)) {
181 return CodeWriter::ForFile(file_path);
182 } else {
183 return nullptr;
184 }
185 }
186
187 #ifdef _WIN32
188
add_list_files(const string & dirname,vector<string> * result)189 static Result<void> add_list_files(const string& dirname, vector<string>* result) {
190 AIDL_FATAL_IF(result == nullptr, dirname);
191
192 WIN32_FIND_DATA find_data;
193 // Look up the first file.
194 // See https://stackoverflow.com/a/14841564/112950 for why we use remove_pointer_t
195 // here.
196 // Note: we need to use a wildcard expression like `\*` to ensure we traverse
197 // the directory. Otherwise Find{First,Next}File will only return the directory
198 // itself and stop.
199 const string path(dirname + "\\*");
200 std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&FindClose)> search_handle(
201 FindFirstFile(path.c_str(), &find_data), FindClose);
202
203 if (search_handle.get() == INVALID_HANDLE_VALUE) {
204 return Error() << "Failed to read directory '" << dirname << "': " << GetLastError();
205 }
206
207 bool has_more_files = true;
208 do {
209 const bool skip = !strcmp(find_data.cFileName, ".") || !strcmp(find_data.cFileName, "..");
210
211 if (!skip) {
212 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
213 if (auto ret = add_list_files(dirname + OS_PATH_SEPARATOR + find_data.cFileName, result);
214 !ret.ok()) {
215 return ret;
216 }
217 } else {
218 result->emplace_back(dirname + OS_PATH_SEPARATOR + find_data.cFileName);
219 }
220 }
221
222 has_more_files = FindNextFile(search_handle.get(), &find_data);
223 if (!has_more_files) {
224 const DWORD err = GetLastError();
225 if (err != ERROR_NO_MORE_FILES) {
226 return Error() << "Failed to read directory entry in '" << dirname << "': " << err;
227 }
228 }
229 } while (has_more_files);
230
231 return Result<void>();
232 }
233 #else
add_list_files(const string & dirname,vector<string> * result)234 static Result<void> add_list_files(const string& dirname, vector<string>* result) {
235 AIDL_FATAL_IF(result == nullptr, dirname);
236 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
237
238 if (dir == nullptr) {
239 return Error() << "Failed to read directory '" << dirname << "': " << strerror(errno);
240 }
241
242 while (true) {
243 errno = 0;
244 struct dirent* ent = readdir(dir.get());
245 if (ent == nullptr) {
246 if (errno != 0) {
247 return Error() << "Failed to read directory entry in '" << dirname
248 << "': " << strerror(errno);
249 }
250 break;
251 }
252
253 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
254 continue;
255 }
256 if (ent->d_type == DT_REG) {
257 result->emplace_back(dirname + OS_PATH_SEPARATOR + ent->d_name);
258 } else if (ent->d_type == DT_DIR) {
259 if (auto ret = add_list_files(dirname + OS_PATH_SEPARATOR + ent->d_name, result); !ret.ok()) {
260 return ret;
261 }
262 }
263 }
264
265 return Result<void>();
266 }
267 #endif
268
ListFiles(const string & dir) const269 Result<vector<string>> IoDelegate::ListFiles(const string& dir) const {
270 vector<string> result;
271 if (auto ret = add_list_files(dir, &result); !ret.ok()) {
272 return ret.error();
273 }
274 return result;
275 }
276
CleanPath(const string & path)277 string IoDelegate::CleanPath(const string& path) {
278 if (base::StartsWith(path, string{'.', OS_PATH_SEPARATOR})) {
279 return path.substr(2);
280 }
281 return path;
282 }
283
284 } // namespace android
285 } // namespace aidl
286