xref: /aosp_15_r20/art/odrefresh/odr_fs_utils.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "odr_fs_utils.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <dirent.h>
20*795d594fSAndroid Build Coastguard Worker #include <ftw.h>
21*795d594fSAndroid Build Coastguard Worker #include <string.h>
22*795d594fSAndroid Build Coastguard Worker #include <sys/stat.h>
23*795d594fSAndroid Build Coastguard Worker #include <sys/statvfs.h>
24*795d594fSAndroid Build Coastguard Worker #include <unistd.h>
25*795d594fSAndroid Build Coastguard Worker 
26*795d594fSAndroid Build Coastguard Worker #include <iosfwd>
27*795d594fSAndroid Build Coastguard Worker #include <memory>
28*795d594fSAndroid Build Coastguard Worker #include <ostream>
29*795d594fSAndroid Build Coastguard Worker #include <queue>
30*795d594fSAndroid Build Coastguard Worker #include <string>
31*795d594fSAndroid Build Coastguard Worker #include <string_view>
32*795d594fSAndroid Build Coastguard Worker #include <type_traits>
33*795d594fSAndroid Build Coastguard Worker #include <vector>
34*795d594fSAndroid Build Coastguard Worker 
35*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
36*795d594fSAndroid Build Coastguard Worker #include <android-base/macros.h>
37*795d594fSAndroid Build Coastguard Worker #include <android-base/strings.h>
38*795d594fSAndroid Build Coastguard Worker #include <base/os.h>
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker namespace art {
41*795d594fSAndroid Build Coastguard Worker namespace odrefresh {
42*795d594fSAndroid Build Coastguard Worker 
43*795d594fSAndroid Build Coastguard Worker // Callback for use with nftw(3) to assist with clearing files and sub-directories.
44*795d594fSAndroid Build Coastguard Worker // This method removes files and directories below the top-level directory passed to nftw().
NftwCleanUpCallback(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)45*795d594fSAndroid Build Coastguard Worker static int NftwCleanUpCallback(const char* fpath,
46*795d594fSAndroid Build Coastguard Worker                                [[maybe_unused]] const struct stat* sb,
47*795d594fSAndroid Build Coastguard Worker                                int typeflag,
48*795d594fSAndroid Build Coastguard Worker                                struct FTW* ftwbuf) {
49*795d594fSAndroid Build Coastguard Worker   switch (typeflag) {
50*795d594fSAndroid Build Coastguard Worker     case FTW_F:
51*795d594fSAndroid Build Coastguard Worker       return unlink(fpath);
52*795d594fSAndroid Build Coastguard Worker     case FTW_DP:
53*795d594fSAndroid Build Coastguard Worker       return (ftwbuf->level == 0) ? 0 : rmdir(fpath);
54*795d594fSAndroid Build Coastguard Worker     default:
55*795d594fSAndroid Build Coastguard Worker       return -1;
56*795d594fSAndroid Build Coastguard Worker   }
57*795d594fSAndroid Build Coastguard Worker }
58*795d594fSAndroid Build Coastguard Worker 
RemoveDirectory(const std::string & dir_path)59*795d594fSAndroid Build Coastguard Worker WARN_UNUSED bool RemoveDirectory(const std::string& dir_path) {
60*795d594fSAndroid Build Coastguard Worker   if (!OS::DirectoryExists(dir_path.c_str())) {
61*795d594fSAndroid Build Coastguard Worker     return true;
62*795d594fSAndroid Build Coastguard Worker   }
63*795d594fSAndroid Build Coastguard Worker 
64*795d594fSAndroid Build Coastguard Worker   static constexpr int kMaxDescriptors = 4;  // Limit the need for nftw() to re-open descriptors.
65*795d594fSAndroid Build Coastguard Worker   if (nftw(dir_path.c_str(), NftwCleanUpCallback, kMaxDescriptors, FTW_DEPTH | FTW_MOUNT) != 0) {
66*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to clean-up '" << dir_path << "'";
67*795d594fSAndroid Build Coastguard Worker     return false;
68*795d594fSAndroid Build Coastguard Worker   }
69*795d594fSAndroid Build Coastguard Worker 
70*795d594fSAndroid Build Coastguard Worker   if (rmdir(dir_path.c_str()) != 0) {
71*795d594fSAndroid Build Coastguard Worker     // It's possible that we are not able to remove the directory itself. For example, when
72*795d594fSAndroid Build Coastguard Worker     // odrefresh is running in CompOS, the staging dir is prepared beforehand passed to the VM as an
73*795d594fSAndroid Build Coastguard Worker     // FD. In this case, just log and ignore the error. It's okay to keep the directory.
74*795d594fSAndroid Build Coastguard Worker     LOG(WARNING) << "Failed to delete '" << dir_path << "'";
75*795d594fSAndroid Build Coastguard Worker   }
76*795d594fSAndroid Build Coastguard Worker 
77*795d594fSAndroid Build Coastguard Worker   return true;
78*795d594fSAndroid Build Coastguard Worker }
79*795d594fSAndroid Build Coastguard Worker 
EnsureDirectoryExists(const std::string & absolute_path)80*795d594fSAndroid Build Coastguard Worker WARN_UNUSED bool EnsureDirectoryExists(const std::string& absolute_path) {
81*795d594fSAndroid Build Coastguard Worker   if (absolute_path.empty() || absolute_path[0] != '/') {
82*795d594fSAndroid Build Coastguard Worker     LOG(ERROR) << "Path not absolute '" << absolute_path << "'";
83*795d594fSAndroid Build Coastguard Worker     return false;
84*795d594fSAndroid Build Coastguard Worker   }
85*795d594fSAndroid Build Coastguard Worker   std::string path;
86*795d594fSAndroid Build Coastguard Worker   for (const std::string& directory : android::base::Split(absolute_path, "/")) {
87*795d594fSAndroid Build Coastguard Worker     path.append("/").append(directory);
88*795d594fSAndroid Build Coastguard Worker     if (!OS::DirectoryExists(path.c_str())) {
89*795d594fSAndroid Build Coastguard Worker       static constexpr mode_t kDirectoryMode = S_IRWXU | S_IRGRP | S_IXGRP| S_IROTH | S_IXOTH;
90*795d594fSAndroid Build Coastguard Worker       if (mkdir(path.c_str(), kDirectoryMode) != 0) {
91*795d594fSAndroid Build Coastguard Worker         PLOG(ERROR) << "Could not create directory: " << path;
92*795d594fSAndroid Build Coastguard Worker         return false;
93*795d594fSAndroid Build Coastguard Worker       }
94*795d594fSAndroid Build Coastguard Worker     }
95*795d594fSAndroid Build Coastguard Worker   }
96*795d594fSAndroid Build Coastguard Worker   return true;
97*795d594fSAndroid Build Coastguard Worker }
98*795d594fSAndroid Build Coastguard Worker 
GetFreeSpace(const std::string & path,uint64_t * bytes)99*795d594fSAndroid Build Coastguard Worker bool GetFreeSpace(const std::string& path, uint64_t* bytes) {
100*795d594fSAndroid Build Coastguard Worker   struct statvfs sv;
101*795d594fSAndroid Build Coastguard Worker   if (statvfs(path.c_str(), &sv) != 0) {
102*795d594fSAndroid Build Coastguard Worker     PLOG(ERROR) << "statvfs '" << path << "'";
103*795d594fSAndroid Build Coastguard Worker     return false;
104*795d594fSAndroid Build Coastguard Worker   }
105*795d594fSAndroid Build Coastguard Worker   *bytes = sv.f_bfree * sv.f_bsize;
106*795d594fSAndroid Build Coastguard Worker   return true;
107*795d594fSAndroid Build Coastguard Worker }
108*795d594fSAndroid Build Coastguard Worker 
GetUsedSpace(const std::string & path,uint64_t * bytes)109*795d594fSAndroid Build Coastguard Worker bool GetUsedSpace(const std::string& path, uint64_t* bytes) {
110*795d594fSAndroid Build Coastguard Worker   static constexpr std::string_view kCurrentDirectory{"."};
111*795d594fSAndroid Build Coastguard Worker   static constexpr std::string_view kParentDirectory{".."};
112*795d594fSAndroid Build Coastguard Worker   static constexpr size_t kBytesPerBlock = 512;  // see manual page for stat(2).
113*795d594fSAndroid Build Coastguard Worker 
114*795d594fSAndroid Build Coastguard Worker   uint64_t file_bytes = 0;
115*795d594fSAndroid Build Coastguard Worker   std::queue<std::string> unvisited;
116*795d594fSAndroid Build Coastguard Worker   unvisited.push(path);
117*795d594fSAndroid Build Coastguard Worker   while (!unvisited.empty()) {
118*795d594fSAndroid Build Coastguard Worker     std::string current = unvisited.front();
119*795d594fSAndroid Build Coastguard Worker     unvisited.pop();
120*795d594fSAndroid Build Coastguard Worker     std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(current.c_str()), closedir);
121*795d594fSAndroid Build Coastguard Worker     if (!dir) {
122*795d594fSAndroid Build Coastguard Worker       continue;
123*795d594fSAndroid Build Coastguard Worker     }
124*795d594fSAndroid Build Coastguard Worker     for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
125*795d594fSAndroid Build Coastguard Worker       std::string_view name{entity->d_name};
126*795d594fSAndroid Build Coastguard Worker       if (name == kCurrentDirectory || name == kParentDirectory) {
127*795d594fSAndroid Build Coastguard Worker         continue;
128*795d594fSAndroid Build Coastguard Worker       }
129*795d594fSAndroid Build Coastguard Worker       std::string entity_name = current + "/" + entity->d_name;
130*795d594fSAndroid Build Coastguard Worker       if (entity->d_type == DT_DIR) {
131*795d594fSAndroid Build Coastguard Worker         unvisited.push(entity_name.c_str());
132*795d594fSAndroid Build Coastguard Worker       } else if (entity->d_type == DT_REG) {
133*795d594fSAndroid Build Coastguard Worker         struct stat sb;
134*795d594fSAndroid Build Coastguard Worker         if (stat(entity_name.c_str(), &sb) != 0) {
135*795d594fSAndroid Build Coastguard Worker           PLOG(ERROR) << "Failed to stat() file " << entity_name;
136*795d594fSAndroid Build Coastguard Worker           continue;
137*795d594fSAndroid Build Coastguard Worker         }
138*795d594fSAndroid Build Coastguard Worker         file_bytes += sb.st_blocks * kBytesPerBlock;
139*795d594fSAndroid Build Coastguard Worker       }
140*795d594fSAndroid Build Coastguard Worker     }
141*795d594fSAndroid Build Coastguard Worker   }
142*795d594fSAndroid Build Coastguard Worker   *bytes = file_bytes;
143*795d594fSAndroid Build Coastguard Worker   return true;
144*795d594fSAndroid Build Coastguard Worker }
145*795d594fSAndroid Build Coastguard Worker 
146*795d594fSAndroid Build Coastguard Worker }  // namespace odrefresh
147*795d594fSAndroid Build Coastguard Worker }  // namespace art
148