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