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 #include <android-base/strings.h>
27
28 #include "common/libs/utils/files.h"
29 #include "common/libs/utils/result.h"
30 #include "common/libs/utils/subprocess.h"
31 #include "host/commands/assemble_cvd/flags.h"
32
33 namespace cuttlefish {
34 namespace {
35
CleanPriorFiles(const std::string & path,const std::set<std::string> & preserving)36 Result<void> CleanPriorFiles(const std::string& path,
37 const std::set<std::string>& preserving) {
38 if (preserving.count(cpp_basename(path))) {
39 LOG(DEBUG) << "Preserving: " << path;
40 return {};
41 }
42 struct stat statbuf;
43 if (lstat(path.c_str(), &statbuf) < 0) {
44 int error_num = errno;
45 if (error_num == ENOENT) {
46 return {};
47 } else {
48 return CF_ERRNO("Could not stat \"" << path);
49 }
50 }
51 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
52 LOG(DEBUG) << "Deleting: " << path;
53 if (unlink(path.c_str()) < 0) {
54 return CF_ERRNO("Could not unlink \"" << path << "\"");
55 }
56 return {};
57 }
58 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
59 if (!dir) {
60 return CF_ERRNO("Could not clean \"" << path << "\"");
61 }
62 for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
63 std::string entity_name(entity->d_name);
64 if (entity_name == "." || entity_name == "..") {
65 continue;
66 }
67 std::string entity_path = path + "/" + entity_name;
68 CF_EXPECT(CleanPriorFiles(entity_path.c_str(), preserving),
69 "CleanPriorFiles for \""
70 << path << "\" failed on recursing into \"" << entity_path
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 return CF_ERRNO("Could not rmdir \"" << path << "\"");
77 }
78 }
79 return {};
80 }
81
CleanPriorFiles(const std::vector<std::string> & paths,const std::set<std::string> & preserving)82 Result<void> CleanPriorFiles(const std::vector<std::string>& paths,
83 const std::set<std::string>& preserving) {
84 std::vector<std::string> prior_dirs;
85 std::vector<std::string> prior_files;
86 for (const auto& path : paths) {
87 struct stat statbuf;
88 if (stat(path.c_str(), &statbuf) < 0) {
89 if (errno == ENOENT) {
90 continue; // it doesn't exist yet, so there is no work to do
91 }
92 return CF_ERRNO("Could not stat \"" << path << "\"");
93 }
94 bool is_directory = (statbuf.st_mode & S_IFMT) == S_IFDIR;
95 (is_directory ? prior_dirs : prior_files).emplace_back(path);
96 }
97 LOG(DEBUG) << fmt::format("Prior dirs: {}", fmt::join(prior_dirs, ", "));
98 LOG(DEBUG) << fmt::format("Prior files: {}", fmt::join(prior_files, ", "));
99
100 if (prior_dirs.size() > 0 || prior_files.size() > 0) {
101 Command lsof("lsof");
102 lsof.AddParameter("-t");
103 for (const auto& prior_dir : prior_dirs) {
104 lsof.AddParameter("+D").AddParameter(prior_dir);
105 }
106 for (const auto& prior_file : prior_files) {
107 lsof.AddParameter(prior_file);
108 }
109
110 std::string lsof_out;
111 std::string lsof_err;
112 int rval =
113 RunWithManagedStdio(std::move(lsof), nullptr, &lsof_out, &lsof_err);
114 if (rval != 0 && !lsof_err.empty()) {
115 LOG(ERROR) << "Failed to run `lsof`, received message: " << lsof_err;
116 }
117 auto pids = android::base::Split(lsof_out, "\n");
118 CF_EXPECTF(
119 lsof_out.empty(),
120 "Instance directory files in use. Try `cvd reset`? Observed PIDs: {}",
121 fmt::join(pids, ", "));
122 }
123
124 for (const auto& path : paths) {
125 CF_EXPECT(CleanPriorFiles(path, preserving),
126 "CleanPriorFiles failed for \"" << path << "\"");
127 }
128 return {};
129 }
130
131 } // namespace
132
CleanPriorFiles(const std::set<std::string> & preserving,const std::vector<std::string> & clean_dirs)133 Result<void> CleanPriorFiles(const std::set<std::string>& preserving,
134 const std::vector<std::string>& clean_dirs) {
135 std::vector<std::string> paths = {
136 // The environment file
137 GetCuttlefishEnvPath(),
138 // The global link to the config file
139 GetGlobalConfigFileLink(),
140 };
141 paths.insert(paths.end(), clean_dirs.begin(), clean_dirs.end());
142 using android::base::Join;
143 CF_EXPECT(CleanPriorFiles(paths, preserving),
144 "CleanPriorFiles("
145 << "paths = {" << Join(paths, ", ") << "}, "
146 << "preserving = {" << Join(preserving, ", ") << "}) failed");
147 return {};
148 }
149
150 } // namespace cuttlefish
151