Apache Mesos
rm.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 #ifndef __STOUT_OS_WINDOWS_RM_HPP__
13 #define __STOUT_OS_WINDOWS_RM_HPP__
14 
15 #include <stdio.h>
16 
17 #include <string>
18 
19 #include <stout/error.hpp>
20 #include <stout/nothing.hpp>
21 #include <stout/try.hpp>
22 #include <stout/windows.hpp>
23 
24 #include <stout/os/stat.hpp>
25 #include <stout/windows/os.hpp>
26 
28 
29 
30 namespace internal {
31 namespace windows {
32 
33 // NOTE: File and directory deletion on Windows is an asynchronous operation,
34 // and there is no built-in way to "wait" on the deletion. So we wait by
35 // checking for the path's existence until there is a "file not found" error.
36 // Until the file is actually deleted, this will loop on an access denied error
37 // (the file exists but has been marked for deletion).
38 inline Try<Nothing> wait_on_delete(const std::string& path)
39 {
40  // Try for 1 second in 10 intervals of 100 ms.
41  for (int i = 0; i < 10; ++i) {
42  // This should always fail if the file has been marked for deletion.
43  const DWORD attributes =
44  ::GetFileAttributesW(::internal::windows::longpath(path).data());
45  CHECK_EQ(attributes, INVALID_FILE_ATTRIBUTES);
46  const DWORD error = ::GetLastError();
47 
48  if (error == ERROR_ACCESS_DENIED) {
49  LOG(WARNING) << "Waiting for file " << path << " to be deleted";
50  os::sleep(Milliseconds(100));
51  } else if (error == ERROR_FILE_NOT_FOUND) {
52  // The file is truly gone, stop waiting.
53  return Nothing();
54  } else {
55  return WindowsError(error);
56  }
57  }
58 
59  return Error("Timed out when waiting for file " + path + " to be deleted");
60 }
61 
62 } // namespace windows {
63 } // namespace internal {
64 
65 namespace os {
66 
67 inline Try<Nothing> rm(const std::string& path)
68 {
69  // This function uses either `RemoveDirectory` or `DeleteFile` to remove the
70  // actual filesystem entry. These WinAPI functions are being used instead of
71  // the POSIX `remove` because their behavior when it comes to symbolic links
72  // is well documented in MSDN[1][2]. Whenever called on a symbolic link, both
73  // `RemoveDirectory` and `DeleteFile` act on the symlink itself, rather than
74  // its target.
75  //
76  // Because `RemoveDirectory` fails if the specified path is not an empty
77  // directory, its behavior is consistent with the POSIX[3] `remove`[3] (which
78  // uses `rmdir`[4]).
79  //
80  // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx
81  // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa365682(v=vs.85).aspx#deletefile_and_deletefiletransacted
82  // [3] http://pubs.opengroup.org/onlinepubs/009695399/functions/remove.html
83  // [4] http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html
84  const std::wstring longpath = ::internal::windows::longpath(path);
85  const BOOL result = os::stat::isdir(path)
86  ? ::RemoveDirectoryW(longpath.data())
87  : ::DeleteFileW(longpath.data());
88 
89  if (!result) {
90  return WindowsError();
91  }
92 
93  // This wait is necessary because the `RemoveDirectory` API does not know to
94  // wait for pending deletions of files in the directory, and can otherwise
95  // immediately fail with "directory not empty" if there still exists a marked
96  // for deletion but not yet deleted file. By making waiting synchronously, we
97  // gain the behavior of the POSIX API.
99  if (deleted.isError()) {
100  return Error("wait_on_delete failed " + deleted.error());
101  }
102 
103  return Nothing();
104 }
105 
106 } // namespace os {
107 
108 #endif // __STOUT_OS_WINDOWS_RM_HPP__
Definition: path.hpp:26
Definition: nothing.hpp:16
Definition: errorbase.hpp:35
Try< Nothing > rm(const std::string &path)
Definition: rm.hpp:26
Definition: check.hpp:33
Try< Nothing > sleep(const Duration &duration)
Definition: os.hpp:234
Definition: error.hpp:106
Definition: posix_signalhandler.hpp:23
Try< Nothing > wait_on_delete(const std::string &path)
Definition: rm.hpp:38
Definition: duration.hpp:193
static Try error(const E &e)
Definition: try.hpp:42
Definition: attributes.hpp:24
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