1 /*
2 * Copyright (C) 2020 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 #include "host/commands/assemble_cvd/clean.h"
17
18 #include <dirent.h>
19 #include <errno.h>
20 #include <sys/stat.h>
21
22 #include <regex>
23 #include <vector>
24
25 #include <android-base/logging.h>
26
27 #include "host/commands/assemble_cvd/flags.h"
28 #include "common/libs/utils/files.h"
29
30 namespace cuttlefish {
31 namespace {
32
CleanPriorFiles(const std::string & path,const std::set<std::string> & preserving)33 bool CleanPriorFiles(const std::string& path, const std::set<std::string>& preserving) {
34 if (preserving.count(cpp_basename(path))) {
35 LOG(DEBUG) << "Preserving: " << path;
36 return true;
37 }
38 struct stat statbuf;
39 if (lstat(path.c_str(), &statbuf) < 0) {
40 int error_num = errno;
41 if (error_num == ENOENT) {
42 return true;
43 } else {
44 LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
45 return false;
46 }
47 }
48 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
49 LOG(DEBUG) << "Deleting: " << path;
50 if (unlink(path.c_str()) < 0) {
51 int error_num = errno;
52 LOG(ERROR) << "Could not unlink \"" << path << "\", error was " << strerror(error_num);
53 return false;
54 }
55 return true;
56 }
57 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
58 if (!dir) {
59 int error_num = errno;
60 LOG(ERROR) << "Could not clean \"" << path << "\": error was " << strerror(error_num);
61 return false;
62 }
63 for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
64 std::string entity_name(entity->d_name);
65 if (entity_name == "." || entity_name == "..") {
66 continue;
67 }
68 std::string entity_path = path + "/" + entity_name;
69 if (!CleanPriorFiles(entity_path.c_str(), preserving)) {
70 return false;
71 }
72 }
73 if (rmdir(path.c_str()) < 0) {
74 if (!(errno == EEXIST || errno == ENOTEMPTY)) {
75 // If EEXIST or ENOTEMPTY, probably because a file was preserved
76 int error_num = errno;
77 LOG(ERROR) << "Could not rmdir \"" << path << "\", error was " << strerror(error_num);
78 return false;
79 }
80 }
81 return true;
82 }
83
CleanPriorFiles(const std::vector<std::string> & paths,const std::set<std::string> & preserving)84 bool CleanPriorFiles(const std::vector<std::string>& paths, const std::set<std::string>& preserving) {
85 std::string prior_files;
86 for (auto path : paths) {
87 struct stat statbuf;
88 if (stat(path.c_str(), &statbuf) < 0 && errno != ENOENT) {
89 // If ENOENT, it doesn't exist yet, so there is no work to do'
90 int error_num = errno;
91 LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
92 return false;
93 }
94 bool is_directory = (statbuf.st_mode & S_IFMT) == S_IFDIR;
95 prior_files += (is_directory ? (path + "/*") : path) + " ";
96 }
97 LOG(DEBUG) << "Assuming prior files of " << prior_files;
98 std::string lsof_cmd = "lsof -t " + prior_files + " >/dev/null 2>&1";
99 int rval = std::system(lsof_cmd.c_str());
100 // lsof returns 0 if any of the files are open
101 if (WEXITSTATUS(rval) == 0) {
102 LOG(ERROR) << "Clean aborted: files are in use";
103 return false;
104 }
105 for (const auto& path : paths) {
106 if (!CleanPriorFiles(path, preserving)) {
107 LOG(ERROR) << "Remove of file under \"" << path << "\" failed";
108 return false;
109 }
110 }
111 return true;
112 }
113
114 } // namespace
115
CleanPriorFiles(const std::set<std::string> & preserving,const std::string & assembly_dir,const std::string & instance_dir)116 bool CleanPriorFiles(
117 const std::set<std::string>& preserving,
118 const std::string& assembly_dir,
119 const std::string& instance_dir) {
120 std::vector<std::string> paths = {
121 // Everything in the assembly directory
122 assembly_dir,
123 // The environment file
124 GetCuttlefishEnvPath(),
125 // The global link to the config file
126 GetGlobalConfigFileLink(),
127 };
128
129 std::string runtime_dir_parent = cpp_dirname(AbsolutePath(instance_dir));
130 std::string runtime_dirs_basename = cpp_basename(AbsolutePath(instance_dir));
131
132 std::regex instance_dir_regex("^.+\\.[1-9]\\d*$");
133 for (const auto& path : DirectoryContents(runtime_dir_parent)) {
134 std::string absl_path = runtime_dir_parent + "/" + path;
135 if((path.rfind(runtime_dirs_basename, 0) == 0) && std::regex_match(path, instance_dir_regex) &&
136 DirectoryExists(absl_path)) {
137 paths.push_back(absl_path);
138 }
139 }
140 paths.push_back(instance_dir);
141 return CleanPriorFiles(paths, preserving);
142 }
143
EnsureDirectoryExists(const std::string & directory_path)144 bool EnsureDirectoryExists(const std::string& directory_path) {
145 if (!DirectoryExists(directory_path)) {
146 LOG(DEBUG) << "Setting up " << directory_path;
147 if (mkdir(directory_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
148 && errno != EEXIST) {
149 PLOG(ERROR) << "Failed to create dir: \"" << directory_path << "\" ";
150 return false;
151 }
152 }
153 return true;
154 }
155
156 } // namespace cuttlefish
157