• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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