xref: /aosp_15_r20/system/incremental_delivery/incfs/path.cpp (revision 9190c2a8bd3622b7aa9bd7bfe4b3aec77820f478)
1*9190c2a8SAndroid Build Coastguard Worker /*
2*9190c2a8SAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*9190c2a8SAndroid Build Coastguard Worker  *
4*9190c2a8SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*9190c2a8SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*9190c2a8SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*9190c2a8SAndroid Build Coastguard Worker  *
8*9190c2a8SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*9190c2a8SAndroid Build Coastguard Worker  *
10*9190c2a8SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*9190c2a8SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*9190c2a8SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9190c2a8SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*9190c2a8SAndroid Build Coastguard Worker  * limitations under the License.
15*9190c2a8SAndroid Build Coastguard Worker  */
16*9190c2a8SAndroid Build Coastguard Worker 
17*9190c2a8SAndroid Build Coastguard Worker #include "path.h"
18*9190c2a8SAndroid Build Coastguard Worker 
19*9190c2a8SAndroid Build Coastguard Worker #include <android-base/logging.h>
20*9190c2a8SAndroid Build Coastguard Worker 
21*9190c2a8SAndroid Build Coastguard Worker #include <memory>
22*9190c2a8SAndroid Build Coastguard Worker 
23*9190c2a8SAndroid Build Coastguard Worker #include <dirent.h>
24*9190c2a8SAndroid Build Coastguard Worker #include <errno.h>
25*9190c2a8SAndroid Build Coastguard Worker #include <limits.h>
26*9190c2a8SAndroid Build Coastguard Worker #include <stdlib.h>
27*9190c2a8SAndroid Build Coastguard Worker #include <sys/stat.h>
28*9190c2a8SAndroid Build Coastguard Worker #include <unistd.h>
29*9190c2a8SAndroid Build Coastguard Worker 
30*9190c2a8SAndroid Build Coastguard Worker using namespace std::literals;
31*9190c2a8SAndroid Build Coastguard Worker 
32*9190c2a8SAndroid Build Coastguard Worker namespace android::incfs::path {
33*9190c2a8SAndroid Build Coastguard Worker 
34*9190c2a8SAndroid Build Coastguard Worker namespace {
35*9190c2a8SAndroid Build Coastguard Worker 
36*9190c2a8SAndroid Build Coastguard Worker class CStrWrapper {
37*9190c2a8SAndroid Build Coastguard Worker public:
CStrWrapper(std::string_view sv)38*9190c2a8SAndroid Build Coastguard Worker     CStrWrapper(std::string_view sv) {
39*9190c2a8SAndroid Build Coastguard Worker         if (sv[sv.size()] == '\0') {
40*9190c2a8SAndroid Build Coastguard Worker             mCstr = sv.data();
41*9190c2a8SAndroid Build Coastguard Worker         } else {
42*9190c2a8SAndroid Build Coastguard Worker             mCopy.emplace(sv);
43*9190c2a8SAndroid Build Coastguard Worker             mCstr = mCopy->c_str();
44*9190c2a8SAndroid Build Coastguard Worker         }
45*9190c2a8SAndroid Build Coastguard Worker     }
46*9190c2a8SAndroid Build Coastguard Worker 
47*9190c2a8SAndroid Build Coastguard Worker     CStrWrapper(const CStrWrapper&) = delete;
48*9190c2a8SAndroid Build Coastguard Worker     void operator=(const CStrWrapper&) = delete;
49*9190c2a8SAndroid Build Coastguard Worker     CStrWrapper(CStrWrapper&&) = delete;
50*9190c2a8SAndroid Build Coastguard Worker     void operator=(CStrWrapper&&) = delete;
51*9190c2a8SAndroid Build Coastguard Worker 
get() const52*9190c2a8SAndroid Build Coastguard Worker     const char* get() const { return mCstr; }
operator const char*() const53*9190c2a8SAndroid Build Coastguard Worker     operator const char*() const { return get(); }
54*9190c2a8SAndroid Build Coastguard Worker 
55*9190c2a8SAndroid Build Coastguard Worker private:
56*9190c2a8SAndroid Build Coastguard Worker     const char* mCstr;
57*9190c2a8SAndroid Build Coastguard Worker     std::optional<std::string> mCopy;
58*9190c2a8SAndroid Build Coastguard Worker };
59*9190c2a8SAndroid Build Coastguard Worker 
c_str(std::string_view sv)60*9190c2a8SAndroid Build Coastguard Worker inline CStrWrapper c_str(std::string_view sv) {
61*9190c2a8SAndroid Build Coastguard Worker     return {sv};
62*9190c2a8SAndroid Build Coastguard Worker }
63*9190c2a8SAndroid Build Coastguard Worker 
64*9190c2a8SAndroid Build Coastguard Worker } // namespace
65*9190c2a8SAndroid Build Coastguard Worker 
isAbsolute(std::string_view path)66*9190c2a8SAndroid Build Coastguard Worker bool isAbsolute(std::string_view path) {
67*9190c2a8SAndroid Build Coastguard Worker     return !path.empty() && path[0] == '/';
68*9190c2a8SAndroid Build Coastguard Worker }
69*9190c2a8SAndroid Build Coastguard Worker 
normalize(std::string_view path)70*9190c2a8SAndroid Build Coastguard Worker std::string normalize(std::string_view path) {
71*9190c2a8SAndroid Build Coastguard Worker     if (path.empty()) {
72*9190c2a8SAndroid Build Coastguard Worker         return {};
73*9190c2a8SAndroid Build Coastguard Worker     }
74*9190c2a8SAndroid Build Coastguard Worker     if (path.starts_with("../"sv)) {
75*9190c2a8SAndroid Build Coastguard Worker         return {};
76*9190c2a8SAndroid Build Coastguard Worker     }
77*9190c2a8SAndroid Build Coastguard Worker 
78*9190c2a8SAndroid Build Coastguard Worker     std::string result;
79*9190c2a8SAndroid Build Coastguard Worker     if (isAbsolute(path)) {
80*9190c2a8SAndroid Build Coastguard Worker         path.remove_prefix(1);
81*9190c2a8SAndroid Build Coastguard Worker     } else {
82*9190c2a8SAndroid Build Coastguard Worker         char buffer[PATH_MAX];
83*9190c2a8SAndroid Build Coastguard Worker         if (!::getcwd(buffer, sizeof(buffer))) {
84*9190c2a8SAndroid Build Coastguard Worker             return {};
85*9190c2a8SAndroid Build Coastguard Worker         }
86*9190c2a8SAndroid Build Coastguard Worker         result += buffer;
87*9190c2a8SAndroid Build Coastguard Worker     }
88*9190c2a8SAndroid Build Coastguard Worker 
89*9190c2a8SAndroid Build Coastguard Worker     size_t start = 0;
90*9190c2a8SAndroid Build Coastguard Worker     size_t end = 0;
91*9190c2a8SAndroid Build Coastguard Worker     for (; end != path.npos; start = end + 1) {
92*9190c2a8SAndroid Build Coastguard Worker         end = path.find('/', start);
93*9190c2a8SAndroid Build Coastguard Worker         // Next component, excluding the separator
94*9190c2a8SAndroid Build Coastguard Worker         auto part = path.substr(start, end - start);
95*9190c2a8SAndroid Build Coastguard Worker         if (part.empty() || part == "."sv) {
96*9190c2a8SAndroid Build Coastguard Worker             continue;
97*9190c2a8SAndroid Build Coastguard Worker         }
98*9190c2a8SAndroid Build Coastguard Worker         if (part == ".."sv) {
99*9190c2a8SAndroid Build Coastguard Worker             if (result.empty()) {
100*9190c2a8SAndroid Build Coastguard Worker                 return {};
101*9190c2a8SAndroid Build Coastguard Worker             }
102*9190c2a8SAndroid Build Coastguard Worker             auto lastPos = result.rfind('/');
103*9190c2a8SAndroid Build Coastguard Worker             if (lastPos == result.npos) {
104*9190c2a8SAndroid Build Coastguard Worker                 result.clear();
105*9190c2a8SAndroid Build Coastguard Worker             } else {
106*9190c2a8SAndroid Build Coastguard Worker                 result.resize(lastPos);
107*9190c2a8SAndroid Build Coastguard Worker             }
108*9190c2a8SAndroid Build Coastguard Worker             continue;
109*9190c2a8SAndroid Build Coastguard Worker         }
110*9190c2a8SAndroid Build Coastguard Worker         result += '/';
111*9190c2a8SAndroid Build Coastguard Worker         result += part;
112*9190c2a8SAndroid Build Coastguard Worker     }
113*9190c2a8SAndroid Build Coastguard Worker 
114*9190c2a8SAndroid Build Coastguard Worker     return result;
115*9190c2a8SAndroid Build Coastguard Worker }
116*9190c2a8SAndroid Build Coastguard Worker 
117*9190c2a8SAndroid Build Coastguard Worker static constexpr char fdNameFormat[] = "/proc/self/fd/%d";
118*9190c2a8SAndroid Build Coastguard Worker 
procfsForFd(int fd)119*9190c2a8SAndroid Build Coastguard Worker std::string procfsForFd(int fd) {
120*9190c2a8SAndroid Build Coastguard Worker     char fdNameBuffer[std::size(fdNameFormat) + 11 + 1]; // max int length + '\0'
121*9190c2a8SAndroid Build Coastguard Worker     snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd);
122*9190c2a8SAndroid Build Coastguard Worker     return fdNameBuffer;
123*9190c2a8SAndroid Build Coastguard Worker }
124*9190c2a8SAndroid Build Coastguard Worker 
fromFd(int fd)125*9190c2a8SAndroid Build Coastguard Worker std::string fromFd(int fd) {
126*9190c2a8SAndroid Build Coastguard Worker     char fdNameBuffer[std::size(fdNameFormat) + 11 + 1]; // max int length + '\0'
127*9190c2a8SAndroid Build Coastguard Worker     snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd);
128*9190c2a8SAndroid Build Coastguard Worker 
129*9190c2a8SAndroid Build Coastguard Worker     return readlink(fdNameBuffer);
130*9190c2a8SAndroid Build Coastguard Worker }
131*9190c2a8SAndroid Build Coastguard Worker 
readlink(std::string_view path)132*9190c2a8SAndroid Build Coastguard Worker std::string readlink(std::string_view path) {
133*9190c2a8SAndroid Build Coastguard Worker     static constexpr auto kDeletedSuffix = " (deleted)"sv;
134*9190c2a8SAndroid Build Coastguard Worker 
135*9190c2a8SAndroid Build Coastguard Worker     auto cPath = c_str(path);
136*9190c2a8SAndroid Build Coastguard Worker     std::string res;
137*9190c2a8SAndroid Build Coastguard Worker     // We used to call lstat() here to preallocate the buffer to the exact required size; turns out
138*9190c2a8SAndroid Build Coastguard Worker     // that call is significantly more expensive than anything else, so doing a couple extra
139*9190c2a8SAndroid Build Coastguard Worker     // iterations is worth the savings.
140*9190c2a8SAndroid Build Coastguard Worker     auto bufSize = 256;
141*9190c2a8SAndroid Build Coastguard Worker     for (;;) {
142*9190c2a8SAndroid Build Coastguard Worker         res.resize(bufSize - 1, '\0');
143*9190c2a8SAndroid Build Coastguard Worker         auto size = ::readlink(cPath, &res[0], res.size());
144*9190c2a8SAndroid Build Coastguard Worker         if (size < 0) {
145*9190c2a8SAndroid Build Coastguard Worker             PLOG(ERROR) << "readlink failed for " << path;
146*9190c2a8SAndroid Build Coastguard Worker             return {};
147*9190c2a8SAndroid Build Coastguard Worker         }
148*9190c2a8SAndroid Build Coastguard Worker         if (size >= ssize_t(res.size())) {
149*9190c2a8SAndroid Build Coastguard Worker             // can't tell if the name is exactly that long, or got truncated - just repeat the call.
150*9190c2a8SAndroid Build Coastguard Worker             bufSize *= 2;
151*9190c2a8SAndroid Build Coastguard Worker             continue;
152*9190c2a8SAndroid Build Coastguard Worker         }
153*9190c2a8SAndroid Build Coastguard Worker         res.resize(size);
154*9190c2a8SAndroid Build Coastguard Worker         if (res.ends_with(kDeletedSuffix)) {
155*9190c2a8SAndroid Build Coastguard Worker             res.resize(size - kDeletedSuffix.size());
156*9190c2a8SAndroid Build Coastguard Worker         }
157*9190c2a8SAndroid Build Coastguard Worker         return res;
158*9190c2a8SAndroid Build Coastguard Worker     }
159*9190c2a8SAndroid Build Coastguard Worker }
160*9190c2a8SAndroid Build Coastguard Worker 
preparePathComponent(std::string_view & path,bool trimAll)161*9190c2a8SAndroid Build Coastguard Worker static void preparePathComponent(std::string_view& path, bool trimAll) {
162*9190c2a8SAndroid Build Coastguard Worker     // need to check for double front slash as a single one has a separate meaning in front
163*9190c2a8SAndroid Build Coastguard Worker     while (!path.empty() && path.front() == '/' &&
164*9190c2a8SAndroid Build Coastguard Worker            (trimAll || (path.size() > 1 && path[1] == '/'))) {
165*9190c2a8SAndroid Build Coastguard Worker         path.remove_prefix(1);
166*9190c2a8SAndroid Build Coastguard Worker     }
167*9190c2a8SAndroid Build Coastguard Worker     // for the back we don't care about double-vs-single slash difference
168*9190c2a8SAndroid Build Coastguard Worker     while (path.size() > !trimAll && path.back() == '/') {
169*9190c2a8SAndroid Build Coastguard Worker         path.remove_suffix(1);
170*9190c2a8SAndroid Build Coastguard Worker     }
171*9190c2a8SAndroid Build Coastguard Worker }
172*9190c2a8SAndroid Build Coastguard Worker 
relativize(std::string_view parent,std::string_view nested)173*9190c2a8SAndroid Build Coastguard Worker std::string_view relativize(std::string_view parent, std::string_view nested) {
174*9190c2a8SAndroid Build Coastguard Worker     if (!nested.starts_with(parent)) {
175*9190c2a8SAndroid Build Coastguard Worker         return nested;
176*9190c2a8SAndroid Build Coastguard Worker     }
177*9190c2a8SAndroid Build Coastguard Worker     if (nested.size() == parent.size()) {
178*9190c2a8SAndroid Build Coastguard Worker         return {};
179*9190c2a8SAndroid Build Coastguard Worker     }
180*9190c2a8SAndroid Build Coastguard Worker     if (nested[parent.size()] != '/') {
181*9190c2a8SAndroid Build Coastguard Worker         return nested;
182*9190c2a8SAndroid Build Coastguard Worker     }
183*9190c2a8SAndroid Build Coastguard Worker     auto relative = nested.substr(parent.size());
184*9190c2a8SAndroid Build Coastguard Worker     while (relative.front() == '/') {
185*9190c2a8SAndroid Build Coastguard Worker         relative.remove_prefix(1);
186*9190c2a8SAndroid Build Coastguard Worker     }
187*9190c2a8SAndroid Build Coastguard Worker     return relative;
188*9190c2a8SAndroid Build Coastguard Worker }
189*9190c2a8SAndroid Build Coastguard Worker 
appendNextPath(std::string & res,std::string_view path)190*9190c2a8SAndroid Build Coastguard Worker void details::appendNextPath(std::string& res, std::string_view path) {
191*9190c2a8SAndroid Build Coastguard Worker     preparePathComponent(path, !res.empty());
192*9190c2a8SAndroid Build Coastguard Worker     if (path.empty()) {
193*9190c2a8SAndroid Build Coastguard Worker         return;
194*9190c2a8SAndroid Build Coastguard Worker     }
195*9190c2a8SAndroid Build Coastguard Worker     if (!res.empty() && !res.ends_with('/')) {
196*9190c2a8SAndroid Build Coastguard Worker         res.push_back('/');
197*9190c2a8SAndroid Build Coastguard Worker     }
198*9190c2a8SAndroid Build Coastguard Worker     res += path;
199*9190c2a8SAndroid Build Coastguard Worker }
200*9190c2a8SAndroid Build Coastguard Worker 
splitDirBase(std::string & full)201*9190c2a8SAndroid Build Coastguard Worker std::pair<std::string_view, std::string_view> splitDirBase(std::string& full) {
202*9190c2a8SAndroid Build Coastguard Worker     auto res = std::pair(dirName(full), baseName(full));
203*9190c2a8SAndroid Build Coastguard Worker     if (res.first.data() == full.data()) {
204*9190c2a8SAndroid Build Coastguard Worker         full[res.first.size()] = 0;
205*9190c2a8SAndroid Build Coastguard Worker     }
206*9190c2a8SAndroid Build Coastguard Worker     return res;
207*9190c2a8SAndroid Build Coastguard Worker }
208*9190c2a8SAndroid Build Coastguard Worker 
isEmptyDir(std::string_view dir)209*9190c2a8SAndroid Build Coastguard Worker int isEmptyDir(std::string_view dir) {
210*9190c2a8SAndroid Build Coastguard Worker     const auto d = std::unique_ptr<DIR, decltype(&::closedir)>{::opendir(c_str(dir)), ::closedir};
211*9190c2a8SAndroid Build Coastguard Worker     if (!d) {
212*9190c2a8SAndroid Build Coastguard Worker         return -errno;
213*9190c2a8SAndroid Build Coastguard Worker     }
214*9190c2a8SAndroid Build Coastguard Worker     while (const auto entry = ::readdir(d.get())) {
215*9190c2a8SAndroid Build Coastguard Worker         if (entry->d_type != DT_DIR) {
216*9190c2a8SAndroid Build Coastguard Worker             return -ENOTEMPTY;
217*9190c2a8SAndroid Build Coastguard Worker         }
218*9190c2a8SAndroid Build Coastguard Worker         if (entry->d_name != "."sv && entry->d_name != ".."sv) {
219*9190c2a8SAndroid Build Coastguard Worker             return -ENOTEMPTY;
220*9190c2a8SAndroid Build Coastguard Worker         }
221*9190c2a8SAndroid Build Coastguard Worker     }
222*9190c2a8SAndroid Build Coastguard Worker     return 0;
223*9190c2a8SAndroid Build Coastguard Worker }
224*9190c2a8SAndroid Build Coastguard Worker 
startsWith(std::string_view path,std::string_view prefix)225*9190c2a8SAndroid Build Coastguard Worker bool startsWith(std::string_view path, std::string_view prefix) {
226*9190c2a8SAndroid Build Coastguard Worker     if (!path.starts_with(prefix)) {
227*9190c2a8SAndroid Build Coastguard Worker         return false;
228*9190c2a8SAndroid Build Coastguard Worker     }
229*9190c2a8SAndroid Build Coastguard Worker     return path.size() == prefix.size() || path[prefix.size()] == '/';
230*9190c2a8SAndroid Build Coastguard Worker }
231*9190c2a8SAndroid Build Coastguard Worker 
endsWith(std::string_view path,std::string_view suffix)232*9190c2a8SAndroid Build Coastguard Worker bool endsWith(std::string_view path, std::string_view suffix) {
233*9190c2a8SAndroid Build Coastguard Worker     if (!path.ends_with(suffix)) {
234*9190c2a8SAndroid Build Coastguard Worker         return false;
235*9190c2a8SAndroid Build Coastguard Worker     }
236*9190c2a8SAndroid Build Coastguard Worker     return path.size() == suffix.size() || path[path.size() - suffix.size() - 1] == '/';
237*9190c2a8SAndroid Build Coastguard Worker }
238*9190c2a8SAndroid Build Coastguard Worker 
239*9190c2a8SAndroid Build Coastguard Worker } // namespace android::incfs::path
240