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_POSIX_RMDIR_HPP__
14 #define __STOUT_OS_POSIX_RMDIR_HPP__
15 
16 #include <fts.h>
17 #include <unistd.h>
18 #include <string>
19 
20 #include <glog/logging.h>
21 
22 #include <stout/error.hpp>
23 #include <stout/nothing.hpp>
24 #include <stout/path.hpp>
25 #include <stout/stringify.hpp>
26 #include <stout/try.hpp>
27 
28 #include <stout/os/exists.hpp>
29 
30 
31 namespace os {
32 
33 // By default, recursively deletes a directory akin to: 'rm -r'. If
34 // `recursive` is false, it deletes a directory akin to: 'rmdir'. In
35 // recursive mode, `removeRoot` can be set to false to enable removing
36 // all the files and directories beneath the given root directory, but
37 // not the root directory itself.
38 // Note that this function expects an absolute path.
39 // By default rmdir aborts when an error occurs during the deletion of
40 // any file but if 'continueOnError' is set to true, rmdir logs the error
41 // and continues with the next file.
42 #ifndef __sun // FTS is not available on Solaris.
44  const std::string& directory,
45  bool recursive = true,
46  bool removeRoot = true,
47  bool continueOnError = false)
48 {
49  unsigned int errorCount = 0;
50 
51  if (!recursive) {
52  if (::rmdir(directory.c_str()) < 0) {
53  return ErrnoError();
54  }
55  } else {
56  // NOTE: `fts_open` will not always return `nullptr` if the path does not
57  // exist. We manually induce an error here to indicate that we can't remove
58  // a directory that does not exist.
59  if (!os::exists(directory)) {
60  return ErrnoError(ENOENT);
61  }
62 
63  char* paths[] = {const_cast<char*>(directory.c_str()), nullptr};
64 
65  // Using `FTS_PHYSICAL` here because we need `FTSENT` for the
66  // symbolic link in the directory and not the target it links to.
67  FTS* tree = fts_open(paths, (FTS_NOCHDIR | FTS_PHYSICAL), nullptr);
68  if (tree == nullptr) {
69  return ErrnoError();
70  }
71 
72  FTSENT* node;
73  while ((node = fts_read(tree)) != nullptr) {
74  switch (node->fts_info) {
75  case FTS_DP:
76  // Don't remove the root of the traversal of `removeRoot`
77  // is false.
78  if (!removeRoot && node->fts_level == FTS_ROOTLEVEL) {
79  continue;
80  }
81 
82  if (::rmdir(node->fts_path) < 0 && errno != ENOENT) {
83  if (continueOnError) {
84  LOG(ERROR) << "Failed to delete directory "
85  << path::join(directory, node->fts_path)
86  << ": " << os::strerror(errno);
87  ++errorCount;
88  } else {
90  fts_close(tree);
91  return error;
92  }
93  }
94  break;
95  // `FTS_DEFAULT` would include any file type which is not
96  // explicitly described by any of the other `fts_info` values.
97  case FTS_DEFAULT:
98  case FTS_F:
99  case FTS_SL:
100  // `FTS_SLNONE` should never be the case as we don't set
101  // `FTS_COMFOLLOW` or `FTS_LOGICAL`. Adding here for completion.
102  case FTS_SLNONE:
103  if (::unlink(node->fts_path) < 0 && errno != ENOENT) {
104  if (continueOnError) {
105  LOG(ERROR) << "Failed to delete path "
106  << path::join(directory, node->fts_path)
107  << ": " << os::strerror(errno);
108  ++errorCount;
109  } else {
110  Error error = ErrnoError();
111  fts_close(tree);
112  return error;
113  }
114  }
115  break;
116  default:
117  break;
118  }
119  }
120 
121  if (errno != 0) {
122  Error error = ErrnoError("fts_read failed");
123  fts_close(tree);
124  return error;
125  }
126 
127  if (fts_close(tree) < 0) {
128  return ErrnoError();
129  }
130  }
131 
132  if (errorCount > 0) {
133  return Error("Failed to delete " + stringify(errorCount) + " paths");
134  }
135 
136  return Nothing();
137 }
138 #endif // __sun
139 
140 } // namespace os {
141 
142 
143 #endif // __STOUT_OS_POSIX_RMDIR_HPP__
std::string strerror(int errno_)
A thread-safe version of strerror.
Definition: strerror.hpp:30
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
Definition: errorbase.hpp:35
Definition: try.hpp:34
std::string paths()
Definition: os.hpp:136
Definition: errorbase.hpp:49
std::string join(const std::string &path1, const std::string &path2, const char _separator=os::PATH_SEPARATOR)
Definition: path.hpp:56
std::string error(const std::string &msg, uint32_t code)
std::string stringify(int flags)