Apache Mesos
rmdir.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_WINDOWS_RMDIR_HPP__
14 #define __STOUT_OS_WINDOWS_RMDIR_HPP__
15 
16 #include <string>
17 
18 #include <glog/logging.h>
19 
20 #include <stout/error.hpp>
21 #include <stout/nothing.hpp>
22 #include <stout/strings.hpp>
23 #include <stout/try.hpp>
24 #include <stout/windows.hpp>
25 
26 #include <stout/os/rm.hpp>
27 #include <stout/os/stat.hpp>
28 
29 #include <stout/windows/error.hpp>
30 
32 
33 
34 namespace os {
35 namespace internal {
36 
37 // Recursive version of `RemoveDirectory`. Two things are notable about this
38 // implementation:
39 //
40 // 1. Unlike `rmdir`, this requires Windows-formatted paths, and therefore
41 // should be in the `internal` namespace.
42 // 2. To match the semantics of the POSIX implementation, this function
43 // implements the semantics of `rm -r`, rather than `rmdir`. In particular,
44 // if `path` points at a file, this function will delete it, while a call to
45 // `rmdir` will not.
47  const std::string& path, bool removeRoot, bool continueOnError)
48 {
49  // Base recursion case to delete a symlink or file.
50  //
51  // We explicitly delete symlinks here to handle hanging symlinks. Note that
52  // `os::rm` will correctly delete the symlink, not the target.
53  if (os::stat::islink(path) || os::stat::isfile(path)) {
54  return os::rm(path);
55  }
56 
57  // Recursion case to delete all files and subdirectories of a directory.
58 
59  // Appending a slash here if the path doesn't already have one simplifies
60  // path join logic later, because (unlike Unix) Windows doesn't like double
61  // slashes in paths.
62  const std::string current_path =
63  strings::endsWith(path, "\\") ? path : path + "\\";
64 
65  const std::wstring long_current_path =
66  ::internal::windows::longpath(current_path);
67 
68  // Scope the `search_handle` so that it is closed before we delete the current
69  // directory.
70  {
71  // Get first file matching pattern `X:\path\to\wherever\*`.
72  WIN32_FIND_DATAW found;
73  const std::wstring search_pattern = long_current_path + L"*";
74  const SharedHandle search_handle(
75  ::FindFirstFileW(search_pattern.data(), &found), ::FindClose);
76 
77  if (search_handle.get() == INVALID_HANDLE_VALUE) {
78  return WindowsError(
79  "FindFirstFile failed for pattern " + stringify(search_pattern));
80  }
81 
82  do {
83  // NOTE: do-while is appropriate here because folder is guaranteed to have
84  // at least a file called `.` (and probably also one called `..`).
85  const std::wstring current_file(found.cFileName);
86 
87  const bool is_current_directory = current_file.compare(L".") == 0;
88  const bool is_parent_directory = current_file.compare(L"..") == 0;
89 
90  // Don't try to delete `.` and `..` files in directory.
91  if (is_current_directory || is_parent_directory) {
92  continue;
93  }
94 
95  // Path to remove, note that recursion will call `longpath`.
96  const std::wstring current_absolute_path =
97  long_current_path + current_file;
98 
99  // Depth-first search, deleting files and directories.
101  stringify(current_absolute_path), true, continueOnError);
102 
103  if (removed.isError()) {
104  if (continueOnError) {
105  LOG(WARNING) << "Failed to delete path "
106  << stringify(current_absolute_path) << " with error "
107  << removed.error();
108  } else {
109  return Error(removed.error());
110  }
111  }
112  } while (::FindNextFileW(search_handle.get(), &found));
113 
114  // Check that this loop ended for the right reason.
115  const DWORD error = ::GetLastError();
116  if (error != ERROR_NO_MORE_FILES) {
117  return WindowsError(error);
118  }
119  } // Search Handle is closed when this scope is exited.
120 
121  // Finally, remove current directory unless `removeRoot` is disabled.
122  if (removeRoot) {
123  if (!os::stat::isdir(current_path,
125  return Error("Refusing to rmdir non-directory " + current_path);
126  } else {
127  return os::rm(current_path);
128  }
129  }
130 
131  return Nothing();
132 }
133 
134 } // namespace internal {
135 
136 
137 // By default, recursively deletes a directory akin to: 'rm -r'. If
138 // `recursive` is false, it deletes a directory akin to: 'rmdir'. In
139 // recursive mode, `removeRoot` can be set to false to enable removing
140 // all the files and directories beneath the given root directory, but
141 // not the root directory itself.
142 //
143 // Note that this function expects an absolute path.
144 //
145 // By default rmdir aborts when an error occurs during the deletion
146 // of any file but if continueOnError is set to true, rmdir logs the
147 // error and continues with the next file.
148 inline Try<Nothing> rmdir(
149  const std::string& directory,
150  bool recursive = true,
151  bool removeRoot = true,
152  bool continueOnError = false)
153 {
154  // The API of this function also deletes files symlinks according
155  // to the tests.
156  if (!os::exists(directory)) {
157  return WindowsError(ERROR_FILE_NOT_FOUND);
158  }
159 
160  if (recursive) {
162  directory, removeRoot, continueOnError);
163  } else {
164  if (!os::stat::isdir(directory,
166  return Error("Refusing to rmdir non-directory " + directory);
167  } else {
168  return os::rm(directory);
169  }
170  }
171 }
172 
173 } // namespace os {
174 
175 
176 #endif // __STOUT_OS_WINDOWS_RMDIR_HPP__
bool endsWith(const std::string &s, const std::string &suffix)
Definition: strings.hpp:393
Try< Nothing > rmdir(const std::string &directory, bool recursive=true, bool removeRoot=true, bool continueOnError=false)
Definition: rmdir.hpp:43
bool exists(const std::string &path)
Definition: exists.hpp:26
Definition: nothing.hpp:16
bool isfile(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:87
Definition: errorbase.hpp:35
Try< Nothing > rm(const std::string &path)
Definition: rm.hpp:26
Definition: windows.hpp:78
bool islink(const std::string &path)
Definition: stat.hpp:67
Definition: try.hpp:34
Definition: error.hpp:106
Try< Nothing > recursive_remove_directory(const std::string &path, bool removeRoot, bool continueOnError)
Definition: rmdir.hpp:46
static Try error(const E &e)
Definition: try.hpp:42
bool isError() const
Definition: try.hpp:71
std::string error(const std::string &msg, uint32_t code)
bool isdir(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:78
std::wstring longpath(const std::string &path)
Definition: longpath.hpp:38
std::string stringify(int flags)