1
2 /*
3 * Copyright (C) 2018 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <vintf/FileSystem.h>
19
20 #include <android-base/file.h>
21 #include <android-base/strings.h>
22
23 #include <ranges>
24
25 #include <dirent.h>
26
27 namespace android {
28 namespace vintf {
29 namespace details {
30
fetch(const std::string & path,std::string * fetched,std::string * error) const31 status_t FileSystemImpl::fetch(const std::string& path, std::string* fetched,
32 std::string* error) const {
33 if (!android::base::ReadFileToString(path, fetched, true /* follow_symlinks */)) {
34 int saved_errno = errno;
35 if (error) {
36 *error = "Cannot read " + path + ": " + strerror(saved_errno);
37 }
38 return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
39 }
40 return OK;
41 }
42
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const43 status_t FileSystemImpl::listFiles(const std::string& path, std::vector<std::string>* out,
44 std::string* error) const {
45 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
46 if (!dir) {
47 int saved_errno = errno;
48 if (error) {
49 *error = "Cannot open " + path + ": " + strerror(saved_errno);
50 }
51 return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
52 }
53
54 dirent* dp;
55 while (errno = 0, dp = readdir(dir.get()), dp != nullptr) {
56 if (dp->d_type != DT_DIR) {
57 out->push_back(dp->d_name);
58 }
59 }
60 int saved_errno = errno;
61 if (saved_errno != 0) {
62 if (error) {
63 *error = "Failed while reading directory " + path + ": " + strerror(saved_errno);
64 }
65 }
66 return -saved_errno;
67 }
68
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const69 status_t FileSystemImpl::modifiedTime(const std::string& path, timespec* mtime,
70 std::string* error) const {
71 struct stat stat_buf;
72 if (stat(path.c_str(), &stat_buf) != 0) {
73 int saved_errno = errno;
74 if (error) {
75 *error = "Cannot open " + path + ": " + strerror(saved_errno);
76 }
77 return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
78 }
79 *mtime = stat_buf.st_mtim;
80 return OK;
81 }
82
fetch(const std::string &,std::string *,std::string *) const83 status_t FileSystemNoOp::fetch(const std::string&, std::string*, std::string*) const {
84 return NAME_NOT_FOUND;
85 }
86
listFiles(const std::string &,std::vector<std::string> *,std::string *) const87 status_t FileSystemNoOp::listFiles(const std::string&, std::vector<std::string>*,
88 std::string*) const {
89 return NAME_NOT_FOUND;
90 }
91
modifiedTime(const std::string &,timespec *,std::string *) const92 status_t FileSystemNoOp::modifiedTime(const std::string&, timespec*, std::string*) const {
93 return NAME_NOT_FOUND;
94 }
95
FileSystemUnderPath(const std::string & rootdir)96 FileSystemUnderPath::FileSystemUnderPath(const std::string& rootdir) {
97 mRootDir = rootdir;
98 if (!mRootDir.empty() && mRootDir.back() != '/') {
99 mRootDir.push_back('/');
100 }
101 }
102
fetch(const std::string & path,std::string * fetched,std::string * error) const103 status_t FileSystemUnderPath::fetch(const std::string& path, std::string* fetched,
104 std::string* error) const {
105 return mImpl.fetch(mRootDir + path, fetched, error);
106 }
107
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const108 status_t FileSystemUnderPath::listFiles(const std::string& path, std::vector<std::string>* out,
109 std::string* error) const {
110 return mImpl.listFiles(mRootDir + path, out, error);
111 }
112
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const113 status_t FileSystemUnderPath::modifiedTime(const std::string& path, timespec* mtime,
114 std::string* error) const {
115 return mImpl.modifiedTime(mRootDir + path, mtime, error);
116 }
117
getRootDir() const118 const std::string& FileSystemUnderPath::getRootDir() const {
119 return mRootDir;
120 }
121
enforceTrailingSlash(const std::string & path)122 static std::string enforceTrailingSlash(const std::string& path) {
123 if (android::base::EndsWith(path, '/')) {
124 return path;
125 }
126 return path + "/";
127 }
128
PathReplacingFileSystem(std::unique_ptr<FileSystem> impl,const std::map<std::string,std::string> & path_replacements)129 PathReplacingFileSystem::PathReplacingFileSystem(
130 std::unique_ptr<FileSystem> impl, const std::map<std::string, std::string>& path_replacements)
131 : impl_{std::move(impl)} {
132 // Enforce a trailing slash on the path-to-be-replaced, prevents
133 // the problem (for example) of /foo matching and changing /fooxyz
134 for (const auto& [to_replace, replacement] : path_replacements) {
135 path_replacements_.emplace(enforceTrailingSlash(to_replace),
136 enforceTrailingSlash(replacement));
137 }
138 }
139
fetch(const std::string & path,std::string * fetched,std::string * error) const140 status_t PathReplacingFileSystem::fetch(const std::string& path, std::string* fetched,
141 std::string* error) const {
142 return impl_->fetch(path_replace(path), fetched, error);
143 }
144
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const145 status_t PathReplacingFileSystem::listFiles(const std::string& path, std::vector<std::string>* out,
146 std::string* error) const {
147 return impl_->listFiles(path_replace(path), out, error);
148 }
149
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const150 status_t PathReplacingFileSystem::modifiedTime(const std::string& path, timespec* mtime,
151 std::string* error) const {
152 return impl_->modifiedTime(path_replace(path), mtime, error);
153 }
154
path_replace(std::string_view path) const155 std::string PathReplacingFileSystem::path_replace(std::string_view path) const {
156 // reverse for "longer match wins".
157 for (const auto& [to_replace, replacement] : path_replacements_ | std::views::reverse) {
158 if (android::base::ConsumePrefix(&path, to_replace)) {
159 return replacement + std::string{path};
160 }
161 }
162 return std::string{path};
163 }
164
165 } // namespace details
166 } // namespace vintf
167 } // namespace android
168