• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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