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#else
26#include <sys/stat.h>
27#include <unistd.h>
28#endif
29
30#include <android-base/strings.h>
31
32#include "logging.h"
33#include "os.h"
34
35using std::string;
36using std::unique_ptr;
37using std::vector;
38
39using android::base::Split;
40
41namespace android {
42namespace aidl {
43
44bool IoDelegate::GetAbsolutePath(const string& path, string* absolute_path) {
45#ifdef _WIN32
46
47  char buf[4096];
48  DWORD path_len = GetFullPathName(path.c_str(), sizeof(buf), buf, nullptr);
49  if (path_len <= 0 || path_len >= sizeof(buf)) {
50    LOG(ERROR) << "Failed to GetFullPathName(" << path << ")";
51    return false;
52  }
53  *absolute_path = buf;
54
55  return true;
56
57#else
58
59  if (path.empty()) {
60    LOG(ERROR) << "Giving up on finding an absolute path to represent the "
61                  "empty string.";
62    return false;
63  }
64  if (path[0] == OS_PATH_SEPARATOR) {
65    *absolute_path = path;
66    return true;
67  }
68
69  char buf[4096];
70  if (getcwd(buf, sizeof(buf)) == nullptr) {
71    LOG(ERROR) << "Path of current working directory does not fit in "
72               << sizeof(buf) << " bytes";
73    return false;
74  }
75
76  *absolute_path = buf;
77  *absolute_path += OS_PATH_SEPARATOR;
78  *absolute_path += path;
79  return true;
80#endif
81}
82
83unique_ptr<string> IoDelegate::GetFileContents(
84    const string& filename,
85    const string& content_suffix) const {
86  unique_ptr<string> contents;
87  std::ifstream in(filename, std::ios::in | std::ios::binary);
88  if (!in) {
89    return contents;
90  }
91  contents.reset(new string);
92  in.seekg(0, std::ios::end);
93  ssize_t file_size = in.tellg();
94  contents->resize(file_size + content_suffix.length());
95  in.seekg(0, std::ios::beg);
96  // Read the file contents into the beginning of the string
97  in.read(&(*contents)[0], file_size);
98  // Drop the suffix in at the end.
99  contents->replace(file_size, content_suffix.length(), content_suffix);
100  in.close();
101
102  return contents;
103}
104
105unique_ptr<LineReader> IoDelegate::GetLineReader(
106    const string& file_path) const {
107  return LineReader::ReadFromFile(file_path);
108}
109
110bool IoDelegate::FileIsReadable(const string& path) const {
111#ifdef _WIN32
112  // check that the file exists and is not write-only
113  return (0 == _access(path.c_str(), 0)) &&  // mode 0=exist
114         (0 == _access(path.c_str(), 4));    // mode 4=readable
115#else
116  return (0 == access(path.c_str(), R_OK));
117#endif
118}
119
120bool IoDelegate::CreatedNestedDirs(
121    const string& caller_base_dir,
122    const vector<string>& nested_subdirs) const {
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      LOG(ERROR) << "Error while creating " << base_dir << ": "
142                 << strerror(errno);
143      return false;
144    }
145  }
146  return true;
147}
148
149bool IoDelegate::CreatePathForFile(const string& path) const {
150  if (path.empty()) {
151    return true;
152  }
153
154  string absolute_path;
155  if (!GetAbsolutePath(path, &absolute_path)) {
156    return false;
157  }
158
159  auto directories = Split(absolute_path, string{1u, OS_PATH_SEPARATOR});
160
161  // The "base" directory is just the root of the file system.  On Windows,
162  // this will look like "C:\" but on Unix style file systems we get an empty
163  // string after splitting "/foo" with "/"
164  string base = directories[0];
165  if (base.empty()) {
166    base = "/";
167  }
168  directories.erase(directories.begin());
169
170  // Remove the actual file in question, we're just creating the directory path.
171  directories.pop_back();
172
173  return CreatedNestedDirs(base, directories);
174}
175
176unique_ptr<CodeWriter> IoDelegate::GetCodeWriter(
177    const string& file_path) const {
178  return GetFileWriter(file_path);
179}
180
181void IoDelegate::RemovePath(const std::string& file_path) const {
182#ifdef _WIN32
183  _unlink(file_path.c_str());
184#else
185  unlink(file_path.c_str());
186#endif
187}
188
189}  // namespace android
190}  // namespace aidl
191