/* * Copyright (C) 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "io_delegate.h" #include #include #include #ifdef _WIN32 #include #include #undef ERROR #else #include #include #include #include #endif #include #include "logging.h" #include "os.h" using std::string; using std::unique_ptr; using std::vector; using android::base::Error; using android::base::Result; using android::base::Split; namespace android { namespace aidl { bool IoDelegate::GetAbsolutePath(const string& path, string* absolute_path) { #ifdef _WIN32 char buf[4096]; DWORD path_len = GetFullPathName(path.c_str(), sizeof(buf), buf, nullptr); if (path_len <= 0 || path_len >= sizeof(buf)) { AIDL_ERROR(path) << "Failed to GetFullPathName"; return false; } *absolute_path = buf; return true; #else if (path.empty()) { AIDL_ERROR(path) << "Giving up on finding an absolute path to represent the empty string."; return false; } if (path[0] == OS_PATH_SEPARATOR) { *absolute_path = path; return true; } char buf[4096]; if (getcwd(buf, sizeof(buf)) == nullptr) { AIDL_ERROR(path) << "Path of current working directory does not fit in " << sizeof(buf) << " bytes"; return false; } *absolute_path = buf; *absolute_path += OS_PATH_SEPARATOR; *absolute_path += path; return true; #endif } unique_ptr IoDelegate::GetFileContents( const string& filename, const string& content_suffix) const { unique_ptr contents; std::ifstream in(filename, std::ios::in | std::ios::binary); if (!in) { return contents; } contents.reset(new string); in.seekg(0, std::ios::end); ssize_t file_size = in.tellg(); contents->resize(file_size + content_suffix.length()); in.seekg(0, std::ios::beg); // Read the file contents into the beginning of the string in.read(&(*contents)[0], file_size); // Drop the suffix in at the end. contents->replace(file_size, content_suffix.length(), content_suffix); in.close(); return contents; } unique_ptr IoDelegate::GetLineReader( const string& file_path) const { return LineReader::ReadFromFile(file_path); } bool IoDelegate::FileIsReadable(const string& path) const { #ifdef _WIN32 // check that the file exists and is not write-only return (0 == _access(path.c_str(), 0)) && // mode 0=exist (0 == _access(path.c_str(), 4)); // mode 4=readable #else return (0 == access(path.c_str(), R_OK)); #endif } static bool CreateNestedDirs(const string& caller_base_dir, const vector& nested_subdirs) { string base_dir = caller_base_dir; if (base_dir.empty()) { base_dir = "."; } for (const string& subdir : nested_subdirs) { if (base_dir[base_dir.size() - 1] != OS_PATH_SEPARATOR) { base_dir += OS_PATH_SEPARATOR; } base_dir += subdir; bool success; #ifdef _WIN32 success = _mkdir(base_dir.c_str()) == 0; #else success = mkdir(base_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0; #endif // On darwin when you try to mkdir("/", ...) we get EISDIR. if (!success && (errno != EEXIST && errno != EISDIR)) { AIDL_ERROR(caller_base_dir) << "Error while creating " << base_dir << ": " << strerror(errno); return false; } } return true; } bool IoDelegate::CreateDirForPath(const string& path) const { if (path.empty()) { return true; } string absolute_path; if (!GetAbsolutePath(path, &absolute_path)) { return false; } auto directories = Split(absolute_path, string{OS_PATH_SEPARATOR}); // The "base" directory is just the root of the file system. On Windows, // this will look like "C:\" but on Unix style file systems we get an empty // string after splitting "/foo" with "/" string base = directories[0]; if (base.empty()) { base = "/"; } directories.erase(directories.begin()); // Remove the actual file in question, we're just creating the directory path. bool is_file = path.back() != OS_PATH_SEPARATOR; if (is_file) { directories.pop_back(); } return CreateNestedDirs(base, directories); } unique_ptr IoDelegate::GetCodeWriter( const string& file_path) const { if (CreateDirForPath(file_path)) { return CodeWriter::ForFile(file_path); } else { return nullptr; } } void IoDelegate::RemovePath(const std::string& file_path) const { #ifdef _WIN32 _unlink(file_path.c_str()); #else unlink(file_path.c_str()); #endif } #ifdef _WIN32 Result> IoDelegate::ListFiles(const string&) const { return Error() << "File listing not implemented on Windows"; } #else static Result add_list_files(const string& dirname, vector* result) { AIDL_FATAL_IF(result == nullptr, dirname); std::unique_ptr dir(opendir(dirname.c_str()), closedir); if (dir == nullptr) { return Error() << "Failed to read directory '" << dirname << "': " << strerror(errno); } while (true) { errno = 0; struct dirent* ent = readdir(dir.get()); if (ent == nullptr) { if (errno != 0) { return Error() << "Failed to read directory entry in '" << dirname << "': " << strerror(errno); } break; } if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { continue; } if (ent->d_type == DT_REG) { result->emplace_back(dirname + OS_PATH_SEPARATOR + ent->d_name); } else if (ent->d_type == DT_DIR) { if (auto ret = add_list_files(dirname + OS_PATH_SEPARATOR + ent->d_name, result); !ret.ok()) { return ret; } } } return Result(); } Result> IoDelegate::ListFiles(const string& dir) const { vector result; if (auto ret = add_list_files(dir, &result); !ret.ok()) { return ret.error(); } return result; } #endif } // namespace android } // namespace aidl