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