xref: /aosp_15_r20/system/libvintf/FileSystem.cpp (revision 70a7ec852fcefd15a4fb57f8f183a8b1c3aacb08)
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