1 /* 2 * Copyright (C) 2019 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 "path.h" 18 19 #include <android-base/logging.h> 20 21 #include <memory> 22 23 #include <dirent.h> 24 #include <errno.h> 25 #include <limits.h> 26 #include <stdlib.h> 27 #include <sys/stat.h> 28 #include <unistd.h> 29 30 using namespace std::literals; 31 32 namespace android::incfs::path { 33 34 namespace { 35 36 class CStrWrapper { 37 public: 38 CStrWrapper(std::string_view sv) { 39 if (sv[sv.size()] == '\0') { 40 mCstr = sv.data(); 41 } else { 42 mCopy.emplace(sv); 43 mCstr = mCopy->c_str(); 44 } 45 } 46 47 CStrWrapper(const CStrWrapper&) = delete; 48 void operator=(const CStrWrapper&) = delete; 49 CStrWrapper(CStrWrapper&&) = delete; 50 void operator=(CStrWrapper&&) = delete; 51 52 const char* get() const { return mCstr; } 53 operator const char*() const { return get(); } 54 55 private: 56 const char* mCstr; 57 std::optional<std::string> mCopy; 58 }; 59 60 inline CStrWrapper c_str(std::string_view sv) { 61 return {sv}; 62 } 63 64 } // namespace 65 66 bool isAbsolute(std::string_view path) { 67 return !path.empty() && path[0] == '/'; 68 } 69 70 std::string normalize(std::string_view path) { 71 if (path.empty()) { 72 return {}; 73 } 74 if (path.starts_with("../"sv)) { 75 return {}; 76 } 77 78 std::string result; 79 if (isAbsolute(path)) { 80 path.remove_prefix(1); 81 } else { 82 char buffer[PATH_MAX]; 83 if (!::getcwd(buffer, sizeof(buffer))) { 84 return {}; 85 } 86 result += buffer; 87 } 88 89 size_t start = 0; 90 size_t end = 0; 91 for (; end != path.npos; start = end + 1) { 92 end = path.find('/', start); 93 // Next component, excluding the separator 94 auto part = path.substr(start, end - start); 95 if (part.empty() || part == "."sv) { 96 continue; 97 } 98 if (part == ".."sv) { 99 if (result.empty()) { 100 return {}; 101 } 102 auto lastPos = result.rfind('/'); 103 if (lastPos == result.npos) { 104 result.clear(); 105 } else { 106 result.resize(lastPos); 107 } 108 continue; 109 } 110 result += '/'; 111 result += part; 112 } 113 114 return result; 115 } 116 117 std::string fromFd(int fd) { 118 static constexpr auto kDeletedSuffix = " (deleted)"sv; 119 static constexpr char fdNameFormat[] = "/proc/self/fd/%d"; 120 char fdNameBuffer[std::size(fdNameFormat) + 11 + 1]; // max int length + '\0' 121 snprintf(fdNameBuffer, std::size(fdNameBuffer), fdNameFormat, fd); 122 123 std::string res; 124 // lstat() is supposed to return us exactly the needed buffer size, but 125 // somehow it may also return a smaller (but still >0) st_size field. 126 // That's why let's only use it for the initial estimate. 127 struct stat st = {}; 128 if (::lstat(fdNameBuffer, &st) || st.st_size == 0) { 129 st.st_size = PATH_MAX; 130 } 131 auto bufSize = st.st_size; 132 for (;;) { 133 res.resize(bufSize + 1, '\0'); 134 auto size = ::readlink(fdNameBuffer, &res[0], res.size()); 135 if (size < 0) { 136 PLOG(ERROR) << "readlink failed for " << fdNameBuffer; 137 return {}; 138 } 139 if (size > bufSize) { 140 // File got renamed in between lstat() and readlink() calls? Retry. 141 bufSize *= 2; 142 continue; 143 } 144 res.resize(size); 145 if (res.ends_with(kDeletedSuffix)) { 146 res.resize(size - kDeletedSuffix.size()); 147 } 148 return res; 149 } 150 } 151 152 static void preparePathComponent(std::string_view& path, bool trimFront) { 153 if (trimFront) { 154 while (!path.empty() && path.front() == '/') { 155 path.remove_prefix(1); 156 } 157 } 158 while (!path.empty() && path.back() == '/') { 159 path.remove_suffix(1); 160 } 161 } 162 163 std::string_view relativize(std::string_view parent, std::string_view nested) { 164 if (!nested.starts_with(parent)) { 165 return nested; 166 } 167 if (nested.size() == parent.size()) { 168 return {}; 169 } 170 if (nested[parent.size()] != '/') { 171 return nested; 172 } 173 auto relative = nested.substr(parent.size()); 174 while (relative.front() == '/') { 175 relative.remove_prefix(1); 176 } 177 return relative; 178 } 179 180 void details::appendNextPath(std::string& res, std::string_view path) { 181 preparePathComponent(path, true); 182 if (path.empty()) { 183 return; 184 } 185 if (!res.empty() && !res.ends_with('/')) { 186 res.push_back('/'); 187 } 188 res += path; 189 } 190 191 std::string_view baseName(std::string_view path) { 192 if (path.empty()) { 193 return {}; 194 } 195 if (path == "/"sv) { 196 return "/"sv; 197 } 198 auto pos = path.rfind('/'); 199 while (!path.empty() && pos == path.size() - 1) { 200 path.remove_suffix(1); 201 pos = path.rfind('/'); 202 } 203 if (pos == path.npos) { 204 return path.empty() ? "/"sv : path; 205 } 206 return path.substr(pos + 1); 207 } 208 209 std::string_view dirName(std::string_view path) { 210 if (path.empty()) { 211 return {}; 212 } 213 if (path == "/"sv) { 214 return "/"sv; 215 } 216 const auto pos = path.rfind('/'); 217 if (pos == 0) { 218 return "/"sv; 219 } 220 if (pos == path.npos) { 221 return "."sv; 222 } 223 return path.substr(0, pos); 224 } 225 226 std::pair<std::string_view, std::string_view> splitDirBase(std::string& full) { 227 auto res = std::pair(dirName(full), baseName(full)); 228 if (res.first.data() == full.data()) { 229 full[res.first.size()] = 0; 230 } 231 return res; 232 } 233 234 int isEmptyDir(std::string_view dir) { 235 const auto d = std::unique_ptr<DIR, decltype(&::closedir)>{::opendir(c_str(dir)), ::closedir}; 236 if (!d) { 237 return -errno; 238 } 239 while (const auto entry = ::readdir(d.get())) { 240 if (entry->d_type != DT_DIR) { 241 return -ENOTEMPTY; 242 } 243 if (entry->d_name != "."sv && entry->d_name != ".."sv) { 244 return -ENOTEMPTY; 245 } 246 } 247 return 0; 248 } 249 250 bool startsWith(std::string_view path, std::string_view prefix) { 251 if (!path.starts_with(prefix)) { 252 return false; 253 } 254 return path.size() == prefix.size() || path[prefix.size()] == '/'; 255 } 256 257 bool endsWith(std::string_view path, std::string_view suffix) { 258 if (!path.ends_with(suffix)) { 259 return false; 260 } 261 return path.size() == suffix.size() || path[path.size() - suffix.size() - 1] == '/'; 262 } 263 264 } // namespace android::incfs::path 265