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 "host/commands/assemble_cvd/flags.h"
31
32 namespace cuttlefish {
33 namespace {
34
CleanPriorFiles(const std::string & path,const std::set<std::string> & preserving)35 Result<void> CleanPriorFiles(const std::string& path,
36 const std::set<std::string>& preserving) {
37 if (preserving.count(cpp_basename(path))) {
38 LOG(DEBUG) << "Preserving: " << path;
39 return {};
40 }
41 struct stat statbuf;
42 if (lstat(path.c_str(), &statbuf) < 0) {
43 int error_num = errno;
44 if (error_num == ENOENT) {
45 return {};
46 } else {
47 return CF_ERRNO("Could not stat \"" << path);
48 }
49 }
50 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
51 LOG(DEBUG) << "Deleting: " << path;
52 if (unlink(path.c_str()) < 0) {
53 return CF_ERRNO("Could not unlink \"" << path << "\"");
54 }
55 return {};
56 }
57 std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
58 if (!dir) {
59 return CF_ERRNO("Could not clean \"" << path << "\"");
60 }
61 for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
62 std::string entity_name(entity->d_name);
63 if (entity_name == "." || entity_name == "..") {
64 continue;
65 }
66 std::string entity_path = path + "/" + entity_name;
67 CF_EXPECT(CleanPriorFiles(entity_path.c_str(), preserving),
68 "CleanPriorFiles for \""
69 << path << "\" failed on recursing into \"" << entity_path
70 << "\"");
71 }
72 if (rmdir(path.c_str()) < 0) {
73 if (!(errno == EEXIST || errno == ENOTEMPTY)) {
74 // If EEXIST or ENOTEMPTY, probably because a file was preserved
75 return CF_ERRNO("Could not rmdir \"" << path << "\"");
76 }
77 }
78 return {};
79 }
80
CleanPriorFiles(const std::vector<std::string> & paths,const std::set<std::string> & preserving)81 Result<void> CleanPriorFiles(const std::vector<std::string>& paths,
82 const std::set<std::string>& preserving) {
83 std::string prior_files;
84 for (auto path : paths) {
85 struct stat statbuf;
86 if (stat(path.c_str(), &statbuf) < 0 && errno != ENOENT) {
87 // If ENOENT, it doesn't exist yet, so there is no work to do'
88 return CF_ERRNO("Could not stat \"" << path << "\"");
89 }
90 bool is_directory = (statbuf.st_mode & S_IFMT) == S_IFDIR;
91 prior_files += (is_directory ? (path + "/*") : path) + " ";
92 }
93 LOG(DEBUG) << "Assuming prior files of " << prior_files;
94 std::string lsof_cmd = "lsof -t " + prior_files + " >/dev/null 2>&1";
95 int rval = std::system(lsof_cmd.c_str());
96 // lsof returns 0 if any of the files are open
97 CF_EXPECT(WEXITSTATUS(rval) != 0, "Clean aborted: files are in use");
98 for (const auto& path : paths) {
99 CF_EXPECT(CleanPriorFiles(path, preserving),
100 "CleanPriorFiles failed for \"" << path << "\"");
101 }
102 return {};
103 }
104
105 } // namespace
106
CleanPriorFiles(const std::set<std::string> & preserving,const std::string & assembly_dir,const std::vector<std::string> & instance_dirs)107 Result<void> CleanPriorFiles(const std::set<std::string>& preserving,
108 const std::string& assembly_dir,
109 const std::vector<std::string>& instance_dirs) {
110 std::vector<std::string> paths = {
111 // Everything in the assembly directory
112 assembly_dir,
113 // The environment file
114 GetCuttlefishEnvPath(),
115 // The global link to the config file
116 GetGlobalConfigFileLink(),
117 };
118 paths.insert(paths.end(), instance_dirs.begin(), instance_dirs.end());
119 using android::base::Join;
120 CF_EXPECT(CleanPriorFiles(paths, preserving),
121 "CleanPriorFiles("
122 << "paths = {" << Join(paths, ", ") << "}, "
123 << "preserving = {" << Join(preserving, ", ") << "}) failed");
124 return {};
125 }
126
127 } // namespace cuttlefish
128