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 
30 
31 namespace os {
32 namespace internal {
33 
34 // Recursive version of `RemoveDirectory`. Two things are notable about this
35 // implementation:
36 //
37 // 1. Unlike `rmdir`, this requires Windows-formatted paths, and therefore
38 // should be in the `internal` namespace.
39 // 2. To match the semantics of the POSIX implementation, this function
40 // implements the semantics of `rm -r`, rather than `rmdir`. In particular,
41 // if `path` points at a file, this function will delete it, while a call to
42 // `rmdir` will not.
44  const std::string& path, bool removeRoot, bool continueOnError)
45 {
46  // Base recursion case to delete a symlink or file.
47  //
48  // We explicitly delete symlinks here to handle hanging symlinks. Note that
49  // `os::rm` will correctly delete the symlink, not the target.
50  if (os::stat::islink(path) || os::stat::isfile(path)) {
51  return os::rm(path);
52  }
53 
54  // Recursion case to delete all files and subdirectories of a directory.
55 
56  // Appending a slash here if the path doesn't already have one simplifies
57  // path join logic later, because (unlike Unix) Windows doesn't like double
58  // slashes in paths.
59  const std::string current_path =
60  strings::endsWith(path, "\\") ? path : path + "\\";
61 
62  const std::wstring long_current_path =
63  ::internal::windows::longpath(current_path);
64 
65  // Scope the `search_handle` so that it is closed before we delete the current
66  // directory.
67  {
68  // Get first file matching pattern `X:\path\to\wherever\*`.
69  WIN32_FIND_DATAW found;
70  const std::wstring search_pattern = long_current_path + L"*";
71  const SharedHandle search_handle(
72  ::FindFirstFileW(search_pattern.data(), &found), ::FindClose);
73 
74  if (search_handle.get() == INVALID_HANDLE_VALUE) {
75  return WindowsError(
76  "FindFirstFile failed for pattern " + stringify(search_pattern));
77  }
78 
79  do {
80  // NOTE: do-while is appropriate here because folder is guaranteed to have
81  // at least a file called `.` (and probably also one called `..`).
82  const std::wstring current_file(found.cFileName);
83 
84  const bool is_current_directory = current_file.compare(L".") == 0;
85  const bool is_parent_directory = current_file.compare(L"..") == 0;
86 
87  // Don't try to delete `.` and `..` files in directory.
88  if (is_current_directory || is_parent_directory) {
89  continue;
90  }
91 
92  // Path to remove, note that recursion will call `longpath`.
93  const std::wstring current_absolute_path =
94  long_current_path + current_file;
95 
96  // Depth-first search, deleting files and directories.
98  stringify(current_absolute_path), true, continueOnError);
99 
100  if (removed.isError()) {
101  if (continueOnError) {
102  LOG(WARNING) << "Failed to delete path "
103  << stringify(current_absolute_path) << " with error "
104  << removed.error();
105  } else {
106  return Error(removed.error());
107  }
108  }
109  } while (::FindNextFileW(search_handle.get(), &found));
110 
111  // Check that this loop ended for the right reason.
112  const DWORD error = ::GetLastError();
113  if (error != ERROR_NO_MORE_FILES) {
114  return WindowsError(error);
115  }
116  } // Search Handle is closed when this scope is exited.
117 
118  // Finally, remove current directory unless `removeRoot` is disabled.
119  if (removeRoot) {
120  if (!os::stat::isdir(current_path,
122  return Error("Refusing to rmdir non-directory " + current_path);
123  } else {
124  return os::rm(current_path);
125  }
126  }
127 
128  return Nothing();
129 }
130 
131 } // namespace internal {
132 
133 
134 // By default, recursively deletes a directory akin to: 'rm -r'. If
135 // `recursive` is false, it deletes a directory akin to: 'rmdir'. In
136 // recursive mode, `removeRoot` can be set to false to enable removing
137 // all the files and directories beneath the given root directory, but
138 // not the root directory itself.
139 //
140 // Note that this function expects an absolute path.
141 //
142 // By default rmdir aborts when an error occurs during the deletion
143 // of any file but if continueOnError is set to true, rmdir logs the
144 // error and continues with the next file.
145 inline Try<Nothing> rmdir(
146  const std::string& directory,
147  bool recursive = true,
148  bool removeRoot = true,
149  bool continueOnError = false)
150 {
151  // The API of this function also deletes files symlinks according
152  // to the tests.
153  if (!os::exists(directory)) {
154  return WindowsError(ERROR_FILE_NOT_FOUND);
155  }
156 
157  if (recursive) {
159  directory, removeRoot, continueOnError);
160  } else {
161  if (!os::stat::isdir(directory,
163  return Error("Refusing to rmdir non-directory " + directory);
164  } else {
165  return os::rm(directory);
166  }
167  }
168 }
169 
170 } // namespace os {
171 
172 
173 #endif // __STOUT_OS_WINDOWS_RMDIR_HPP__
bool endsWith(const std::string &s, const std::string &suffix)
Definition: strings.hpp:402
Definition: path.hpp:29
Try< Nothing > rmdir(const std::string &directory, bool recursive=true, bool removeRoot=true, bool continueOnError=false)
Definition: rmdir.hpp:42
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:108
Definition: errorbase.hpp:36
Try< Nothing > rm(const std::string &path)
Definition: rm.hpp:26
Definition: windows.hpp:72
bool islink(const std::string &path)
Definition: stat.hpp:80
Definition: check.hpp:33
Definition: error.hpp:108
Definition: posix_signalhandler.hpp:23
Try< Nothing > recursive_remove_directory(const std::string &path, bool removeRoot, bool continueOnError)
Definition: rmdir.hpp:43
static Try error(const E &e)
Definition: try.hpp:43
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
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:91
std::wstring longpath(const std::string &path)
Definition: longpath.hpp:38
std::string stringify(int flags)