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 <dirent.h>
21 
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 
25 namespace android {
26 namespace vintf {
27 namespace details {
28 
fetch(const std::string & path,std::string * fetched,std::string * error) const29 status_t FileSystemImpl::fetch(const std::string& path, std::string* fetched,
30                                std::string* error) const {
31     if (!android::base::ReadFileToString(path, fetched, true /* follow_symlinks */)) {
32         int saved_errno = errno;
33         if (error) {
34             *error = "Cannot read " + path + ": " + strerror(saved_errno);
35         }
36         return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
37     }
38     return OK;
39 }
40 
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const41 status_t FileSystemImpl::listFiles(const std::string& path, std::vector<std::string>* out,
42                                    std::string* error) const {
43     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
44     if (!dir) {
45         int saved_errno = errno;
46         if (error) {
47             *error = "Cannot open " + path + ": " + strerror(saved_errno);
48         }
49         return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
50     }
51 
52     dirent* dp;
53     while (errno = 0, dp = readdir(dir.get()), dp != nullptr) {
54         if (dp->d_type != DT_DIR) {
55             out->push_back(dp->d_name);
56         }
57     }
58     int saved_errno = errno;
59     if (saved_errno != 0) {
60         if (error) {
61             *error = "Failed while reading directory " + path + ": " + strerror(saved_errno);
62         }
63     }
64     return -saved_errno;
65 }
66 
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const67 status_t FileSystemImpl::modifiedTime(const std::string& path, timespec* mtime,
68                                       std::string* error) const {
69     struct stat stat_buf;
70     if (stat(path.c_str(), &stat_buf) != 0) {
71         int saved_errno = errno;
72         if (error) {
73             *error = "Cannot open " + path + ": " + strerror(saved_errno);
74         }
75         return saved_errno == 0 ? UNKNOWN_ERROR : -saved_errno;
76     }
77     *mtime = stat_buf.st_mtim;
78     return OK;
79 }
80 
fetch(const std::string &,std::string *,std::string *) const81 status_t FileSystemNoOp::fetch(const std::string&, std::string*, std::string*) const {
82     return NAME_NOT_FOUND;
83 }
84 
listFiles(const std::string &,std::vector<std::string> *,std::string *) const85 status_t FileSystemNoOp::listFiles(const std::string&, std::vector<std::string>*,
86                                    std::string*) const {
87     return NAME_NOT_FOUND;
88 }
89 
modifiedTime(const std::string &,timespec *,std::string *) const90 status_t FileSystemNoOp::modifiedTime(const std::string&, timespec*, std::string*) const {
91     return NAME_NOT_FOUND;
92 }
93 
FileSystemUnderPath(const std::string & rootdir)94 FileSystemUnderPath::FileSystemUnderPath(const std::string& rootdir) {
95     mRootDir = rootdir;
96     if (!mRootDir.empty() && mRootDir.back() != '/') {
97         mRootDir.push_back('/');
98     }
99 }
100 
fetch(const std::string & path,std::string * fetched,std::string * error) const101 status_t FileSystemUnderPath::fetch(const std::string& path, std::string* fetched,
102                                     std::string* error) const {
103     return mImpl.fetch(mRootDir + path, fetched, error);
104 }
105 
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const106 status_t FileSystemUnderPath::listFiles(const std::string& path, std::vector<std::string>* out,
107                                         std::string* error) const {
108     return mImpl.listFiles(mRootDir + path, out, error);
109 }
110 
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const111 status_t FileSystemUnderPath::modifiedTime(const std::string& path, timespec* mtime,
112                                            std::string* error) const {
113     return mImpl.modifiedTime(mRootDir + path, mtime, error);
114 }
115 
getRootDir() const116 const std::string& FileSystemUnderPath::getRootDir() const {
117     return mRootDir;
118 }
119 
PathReplacingFileSystem(std::string path_to_replace,std::string path_replacement,std::unique_ptr<FileSystem> impl)120 PathReplacingFileSystem::PathReplacingFileSystem(std::string path_to_replace,
121                                                  std::string path_replacement,
122                                                  std::unique_ptr<FileSystem> impl)
123     : path_to_replace_{std::move(path_to_replace)},
124       path_replacement_{std::move(path_replacement)},
125       impl_{std::move(impl)} {
126     // Enforce a trailing slash on the path-to-be-replaced, prevents
127     // the problem (for example) of /foo matching and changing /fooxyz
128     if (!android::base::EndsWith(path_to_replace_, '/')) {
129         path_to_replace_ += "/";
130     }
131     // Enforce a trailing slash on the replacement path.  This ensures
132     // we are replacing a directory with a directory.
133     if (!android::base::EndsWith(path_replacement_, '/')) {
134         path_replacement_ += "/";
135     }
136 }
137 
fetch(const std::string & path,std::string * fetched,std::string * error) const138 status_t PathReplacingFileSystem::fetch(const std::string& path, std::string* fetched,
139                                         std::string* error) const {
140     return impl_->fetch(path_replace(path), fetched, error);
141 }
142 
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error) const143 status_t PathReplacingFileSystem::listFiles(const std::string& path, std::vector<std::string>* out,
144                                             std::string* error) const {
145     return impl_->listFiles(path_replace(path), out, error);
146 }
147 
modifiedTime(const std::string & path,timespec * mtime,std::string * error) const148 status_t PathReplacingFileSystem::modifiedTime(const std::string& path, timespec* mtime,
149                                                std::string* error) const {
150     return impl_->modifiedTime(path_replace(path), mtime, error);
151 }
152 
path_replace(std::string_view path) const153 std::string PathReplacingFileSystem::path_replace(std::string_view path) const {
154     std::string retstr;
155     if (android::base::ConsumePrefix(&path, path_to_replace_)) {
156         retstr.reserve(path_replacement_.size() + path.size());
157         retstr.append(path_replacement_);
158         retstr.append(path);
159         return retstr;
160     }
161     return std::string{path};
162 }
163 
164 }  // namespace details
165 }  // namespace vintf
166 }  // namespace android
167