blob: 9f3a68bda8d3f09c95096a88ddb3ff18a55d1ea0 [file] [log] [blame]
Orion Hodsonf96c9162021-04-07 10:43:01 +01001/*
2 * Copyright (C) 2021 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 "odr_fs_utils.h"
18
19#include <dirent.h>
20#include <ftw.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <sys/statvfs.h>
24#include <unistd.h>
25
26#include <iosfwd>
27#include <memory>
28#include <ostream>
29#include <queue>
30#include <string>
31#include <string_view>
32#include <type_traits>
33#include <vector>
34
35#include <android-base/logging.h>
36#include <android-base/macros.h>
37#include <android-base/strings.h>
38#include <base/os.h>
39
40namespace art {
41namespace odrefresh {
42
43// Callback for use with nftw(3) to assist with clearing files and sub-directories.
44// This method removes files and directories below the top-level directory passed to nftw().
45static int NftwCleanUpCallback(const char* fpath,
46 const struct stat* sb ATTRIBUTE_UNUSED,
47 int typeflag,
48 struct FTW* ftwbuf) {
49 switch (typeflag) {
50 case FTW_F:
51 return unlink(fpath);
52 case FTW_DP:
53 return (ftwbuf->level == 0) ? 0 : rmdir(fpath);
54 default:
55 return -1;
56 }
57}
58
59WARN_UNUSED bool CleanDirectory(const std::string& dir_path) {
60 if (!OS::DirectoryExists(dir_path.c_str())) {
61 return true;
62 }
63
64 static constexpr int kMaxDescriptors = 4; // Limit the need for nftw() to re-open descriptors.
65 if (nftw(dir_path.c_str(), NftwCleanUpCallback, kMaxDescriptors, FTW_DEPTH | FTW_MOUNT) != 0) {
66 LOG(ERROR) << "Failed to clean-up '" << dir_path << "'";
67 return false;
68 }
69 return true;
70}
71
72WARN_UNUSED bool EnsureDirectoryExists(const std::string& absolute_path) {
73 if (absolute_path.empty() || absolute_path[0] != '/') {
74 LOG(ERROR) << "Path not absolute '" << absolute_path << "'";
75 return false;
76 }
77 std::string path;
78 for (const std::string& directory : android::base::Split(absolute_path, "/")) {
79 path.append("/").append(directory);
80 if (!OS::DirectoryExists(path.c_str())) {
81 static constexpr mode_t kDirectoryMode = S_IRWXU | S_IRGRP | S_IXGRP| S_IROTH | S_IXOTH;
82 if (mkdir(path.c_str(), kDirectoryMode) != 0) {
83 PLOG(ERROR) << "Could not create directory: " << path;
84 return false;
85 }
86 }
87 }
88 return true;
89}
90
91bool GetFreeSpace(const std::string& path, uint64_t* bytes) {
92 struct statvfs sv;
93 if (statvfs(path.c_str(), &sv) != 0) {
94 PLOG(ERROR) << "statvfs '" << path << "'";
95 return false;
96 }
97 *bytes = sv.f_bfree * sv.f_bsize;
98 return true;
99}
100
101bool GetUsedSpace(const std::string& path, uint64_t* bytes) {
102 static constexpr std::string_view kCurrentDirectory{"."};
103 static constexpr std::string_view kParentDirectory{".."};
104 static constexpr size_t kBytesPerBlock = 512; // see manual page for stat(2).
105
106 uint64_t file_bytes = 0;
107 std::queue<std::string> unvisited;
108 unvisited.push(path);
109 while (!unvisited.empty()) {
110 std::string current = unvisited.front();
111 unvisited.pop();
112 std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(current.c_str()), closedir);
113 if (!dir) {
114 continue;
115 }
116 for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
117 std::string_view name{entity->d_name};
118 if (name == kCurrentDirectory || name == kParentDirectory) {
119 continue;
120 }
121 std::string entity_name = current + "/" + entity->d_name;
122 if (entity->d_type == DT_DIR) {
123 unvisited.push(entity_name.c_str());
124 } else if (entity->d_type == DT_REG) {
125 struct stat sb;
126 if (stat(entity_name.c_str(), &sb) != 0) {
127 PLOG(ERROR) << "Failed to stat() file " << entity_name;
128 continue;
129 }
130 file_bytes += sb.st_blocks * kBytesPerBlock;
131 }
132 }
133 }
134 *bytes = file_bytes;
135 return true;
136}
137
138} // namespace odrefresh
139} // namespace art