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 <vector>
22
23 #ifdef _WIN32
24 #include <direct.h>
25 #include <windows.h>
26 #undef ERROR
27 #else
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #endif
33
34 #include <android-base/strings.h>
35
36 #include "logging.h"
37 #include "os.h"
38
39 using std::string;
40 using std::unique_ptr;
41 using std::vector;
42
43 using android::base::Error;
44 using android::base::Result;
45 using android::base::Split;
46 using android::base::StartsWith;
47
48 namespace android {
49 namespace aidl {
50
GetAbsolutePath(const string & path,string * absolute_path)51 bool IoDelegate::GetAbsolutePath(const string& path, string* absolute_path) {
52 #ifdef _WIN32
53
54 char buf[4096];
55 DWORD path_len = GetFullPathName(path.c_str(), sizeof(buf), buf, nullptr);
56 if (path_len <= 0 || path_len >= sizeof(buf)) {
57 AIDL_ERROR(path) << "Failed to GetFullPathName";
58 return false;
59 }
60 *absolute_path = buf;
61
62 return true;
63
64 #else
65
66 if (path.empty()) {
67 AIDL_ERROR(path) << "Giving up on finding an absolute path to represent the empty string.";
68 return false;
69 }
70 if (path[0] == OS_PATH_SEPARATOR) {
71 *absolute_path = path;
72 return true;
73 }
74
75 char buf[4096];
76 if (getcwd(buf, sizeof(buf)) == nullptr) {
77 AIDL_ERROR(path) << "Path of current working directory does not fit in " << sizeof(buf)
78 << " bytes";
79 return false;
80 }
81
82 *absolute_path = buf;
83 *absolute_path += OS_PATH_SEPARATOR;
84 *absolute_path += path;
85 return true;
86 #endif
87 }
88
GetFileContents(const string & filename,const string & content_suffix) const89 unique_ptr<string> IoDelegate::GetFileContents(
90 const string& filename,
91 const string& content_suffix) const {
92 unique_ptr<string> contents;
93 std::ifstream in(filename, std::ios::in | std::ios::binary);
94 if (!in) {
95 return contents;
96 }
97 contents.reset(new string);
98 in.seekg(0, std::ios::end);
99 ssize_t file_size = in.tellg();
100 contents->resize(file_size + content_suffix.length());
101 in.seekg(0, std::ios::beg);
102 // Read the file contents into the beginning of the string
103 in.read(&(*contents)[0], file_size);
104 // Drop the suffix in at the end.
105 contents->replace(file_size, content_suffix.length(), content_suffix);
106 in.close();
107
108 return contents;
109 }
110
FileIsReadable(const string & path) const111 bool IoDelegate::FileIsReadable(const string& path) const {
112 #ifdef _WIN32
113 // check that the file exists and is not write-only
114 return (0 == _access(path.c_str(), 0)) && // mode 0=exist
115 (0 == _access(path.c_str(), 4)); // mode 4=readable
116 #else
117 return (0 == access(path.c_str(), R_OK));
118 #endif
119 }
120
CreateNestedDirs(const string & caller_base_dir,const vector<string> & nested_subdirs)121 static bool CreateNestedDirs(const string& caller_base_dir, const vector<string>& nested_subdirs) {
122 string base_dir = caller_base_dir;
123 if (base_dir.empty()) {
124 base_dir = ".";
125 }
126 for (const string& subdir : nested_subdirs) {
127 if (base_dir[base_dir.size() - 1] != OS_PATH_SEPARATOR) {
128 base_dir += OS_PATH_SEPARATOR;
129 }
130 base_dir += subdir;
131 bool success;
132 #ifdef _WIN32
133 success = _mkdir(base_dir.c_str()) == 0;
134 #else
135 success = mkdir(base_dir.c_str(),
136 S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
137 #endif
138 // On darwin when you try to mkdir("/", ...) we get EISDIR.
139 if (!success && (errno != EEXIST && errno != EISDIR)) {
140 AIDL_ERROR(caller_base_dir) << "Error while creating " << base_dir << ": " << strerror(errno);
141 return false;
142 }
143 }
144 return true;
145 }
146
CreateDirForPath(const string & path) const147 bool IoDelegate::CreateDirForPath(const string& path) const {
148 if (path.empty()) {
149 return true;
150 }
151
152 string absolute_path;
153 if (!GetAbsolutePath(path, &absolute_path)) {
154 return false;
155 }
156
157 auto directories = Split(absolute_path, string{OS_PATH_SEPARATOR});
158
159 // The "base" directory is just the root of the file system. On Windows,
160 // this will look like "C:\" but on Unix style file systems we get an empty
161 // string after splitting "/foo" with "/"
162 string base = directories[0];
163 if (base.empty()) {
164 base = "/";
165 }
166 directories.erase(directories.begin());
167
168 // Remove the actual file in question, we're just creating the directory path.
169 bool is_file = path.back() != OS_PATH_SEPARATOR;
170 if (is_file) {
171 directories.pop_back();
172 }
173
174 return CreateNestedDirs(base, directories);
175 }
176
GetCodeWriter(const string & file_path) const177 unique_ptr<CodeWriter> IoDelegate::GetCodeWriter(
178 const string& file_path) const {
179 if (CreateDirForPath(file_path)) {
180 return CodeWriter::ForFile(file_path);
181 } else {
182 return nullptr;
183 }
184 }
185
186 #ifdef _WIN32
ListFiles(const string &) const187 Result<vector<string>> IoDelegate::ListFiles(const string&) const {
188 return Error() << "File listing not implemented on Windows";
189 }
190
191 #else
add_list_files(const string & dirname,vector<string> * result)192 static Result<void> add_list_files(const string& dirname, vector<string>* result) {
193 AIDL_FATAL_IF(result == nullptr, dirname);
194 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname.c_str()), closedir);
195
196 if (dir == nullptr) {
197 return Error() << "Failed to read directory '" << dirname << "': " << strerror(errno);
198 }
199
200 while (true) {
201 errno = 0;
202 struct dirent* ent = readdir(dir.get());
203 if (ent == nullptr) {
204 if (errno != 0) {
205 return Error() << "Failed to read directory entry in '" << dirname
206 << "': " << strerror(errno);
207 }
208 break;
209 }
210
211 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
212 continue;
213 }
214 if (ent->d_type == DT_REG) {
215 result->emplace_back(dirname + OS_PATH_SEPARATOR + ent->d_name);
216 } else if (ent->d_type == DT_DIR) {
217 if (auto ret = add_list_files(dirname + OS_PATH_SEPARATOR + ent->d_name, result); !ret.ok()) {
218 return ret;
219 }
220 }
221 }
222
223 return Result<void>();
224 }
225
ListFiles(const string & dir) const226 Result<vector<string>> IoDelegate::ListFiles(const string& dir) const {
227 vector<string> result;
228 if (auto ret = add_list_files(dir, &result); !ret.ok()) {
229 return ret.error();
230 }
231 return result;
232 }
233 #endif
234
CleanPath(const string & path)235 string IoDelegate::CleanPath(const string& path) {
236 if (base::StartsWith(path, string{'.', OS_PATH_SEPARATOR})) {
237 return path.substr(2);
238 }
239 return path;
240 }
241
242 } // namespace android
243 } // namespace aidl
244