summaryrefslogtreecommitdiff
path: root/odrefresh/odr_fs_utils.cc
diff options
context:
space:
mode:
Diffstat (limited to 'odrefresh/odr_fs_utils.cc')
-rw-r--r--odrefresh/odr_fs_utils.cc139
1 files changed, 139 insertions, 0 deletions
diff --git a/odrefresh/odr_fs_utils.cc b/odrefresh/odr_fs_utils.cc
new file mode 100644
index 0000000000..9f3a68bda8
--- /dev/null
+++ b/odrefresh/odr_fs_utils.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "odr_fs_utils.h"
+
+#include <dirent.h>
+#include <ftw.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <unistd.h>
+
+#include <iosfwd>
+#include <memory>
+#include <ostream>
+#include <queue>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <base/os.h>
+
+namespace art {
+namespace odrefresh {
+
+// Callback for use with nftw(3) to assist with clearing files and sub-directories.
+// This method removes files and directories below the top-level directory passed to nftw().
+static int NftwCleanUpCallback(const char* fpath,
+ const struct stat* sb ATTRIBUTE_UNUSED,
+ int typeflag,
+ struct FTW* ftwbuf) {
+ switch (typeflag) {
+ case FTW_F:
+ return unlink(fpath);
+ case FTW_DP:
+ return (ftwbuf->level == 0) ? 0 : rmdir(fpath);
+ default:
+ return -1;
+ }
+}
+
+WARN_UNUSED bool CleanDirectory(const std::string& dir_path) {
+ if (!OS::DirectoryExists(dir_path.c_str())) {
+ return true;
+ }
+
+ static constexpr int kMaxDescriptors = 4; // Limit the need for nftw() to re-open descriptors.
+ if (nftw(dir_path.c_str(), NftwCleanUpCallback, kMaxDescriptors, FTW_DEPTH | FTW_MOUNT) != 0) {
+ LOG(ERROR) << "Failed to clean-up '" << dir_path << "'";
+ return false;
+ }
+ return true;
+}
+
+WARN_UNUSED bool EnsureDirectoryExists(const std::string& absolute_path) {
+ if (absolute_path.empty() || absolute_path[0] != '/') {
+ LOG(ERROR) << "Path not absolute '" << absolute_path << "'";
+ return false;
+ }
+ std::string path;
+ for (const std::string& directory : android::base::Split(absolute_path, "/")) {
+ path.append("/").append(directory);
+ if (!OS::DirectoryExists(path.c_str())) {
+ static constexpr mode_t kDirectoryMode = S_IRWXU | S_IRGRP | S_IXGRP| S_IROTH | S_IXOTH;
+ if (mkdir(path.c_str(), kDirectoryMode) != 0) {
+ PLOG(ERROR) << "Could not create directory: " << path;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool GetFreeSpace(const std::string& path, uint64_t* bytes) {
+ struct statvfs sv;
+ if (statvfs(path.c_str(), &sv) != 0) {
+ PLOG(ERROR) << "statvfs '" << path << "'";
+ return false;
+ }
+ *bytes = sv.f_bfree * sv.f_bsize;
+ return true;
+}
+
+bool GetUsedSpace(const std::string& path, uint64_t* bytes) {
+ static constexpr std::string_view kCurrentDirectory{"."};
+ static constexpr std::string_view kParentDirectory{".."};
+ static constexpr size_t kBytesPerBlock = 512; // see manual page for stat(2).
+
+ uint64_t file_bytes = 0;
+ std::queue<std::string> unvisited;
+ unvisited.push(path);
+ while (!unvisited.empty()) {
+ std::string current = unvisited.front();
+ unvisited.pop();
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(current.c_str()), closedir);
+ if (!dir) {
+ continue;
+ }
+ for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
+ std::string_view name{entity->d_name};
+ if (name == kCurrentDirectory || name == kParentDirectory) {
+ continue;
+ }
+ std::string entity_name = current + "/" + entity->d_name;
+ if (entity->d_type == DT_DIR) {
+ unvisited.push(entity_name.c_str());
+ } else if (entity->d_type == DT_REG) {
+ struct stat sb;
+ if (stat(entity_name.c_str(), &sb) != 0) {
+ PLOG(ERROR) << "Failed to stat() file " << entity_name;
+ continue;
+ }
+ file_bytes += sb.st_blocks * kBytesPerBlock;
+ }
+ }
+ }
+ *bytes = file_bytes;
+ return true;
+}
+
+} // namespace odrefresh
+} // namespace art