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