CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sync_union_overlayfs.cc
Go to the documentation of this file.
1 
5 #define __STDC_FORMAT_MACROS
6 
7 #include "sync_union_overlayfs.h"
8 
9 #include <sys/capability.h>
10 
11 #include <string>
12 #include <vector>
13 
14 #include "sync_mediator.h"
15 #include "sync_union.h"
16 #include "util/exception.h"
17 #include "util/fs_traversal.h"
18 #include "util/shared_ptr.h"
19 
20 namespace publish {
21 
23  const string &rdonly_path,
24  const string &union_path,
25  const string &scratch_path)
26  : SyncUnion(mediator, rdonly_path, union_path, scratch_path)
27  , hardlink_lower_inode_(0) { }
28 
30  // trying to obtain CAP_SYS_ADMIN to read 'trusted' xattrs in the scratch
31  // directory of an OverlayFS installation
33 }
34 
36  /*const*/ cap_value_t cap = CAP_SYS_ADMIN; // is non-const as cap_set_flag()
37  // expects a non-const pointer
38  // on RHEL 5 and older
39 
40 // do sanity-check if supported in <sys/capability.h> otherwise just pray...
41 // Note: CAP_SYS_ADMIN is a rather common capability and is very likely to be
42 // supported by all our target systems. If it is not, one of the next
43 // commands will fail with a less descriptive error message.
44 #ifdef CAP_IS_SUPPORTED
45  if (!CAP_IS_SUPPORTED(cap)) {
46  LogCvmfs(kLogUnionFs, kLogStderr, "System doesn't support CAP_SYS_ADMIN");
47  return false;
48  }
49 #endif
50 
51  if (caps == NULL) {
53  "Failed to obtain capability state "
54  "of current process (errno: %d)",
55  errno);
56  return false;
57  }
58 
59  cap_flag_value_t cap_state;
60  if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &cap_state) != 0) {
62  "Failed to check effective set for "
63  "CAP_SYS_ADMIN (errno: %d)",
64  errno);
65  return false;
66  }
67 
68  if (cap_state == CAP_SET) {
69  LogCvmfs(kLogUnionFs, kLogDebug, "CAP_SYS_ADMIN is already effective");
70  return true;
71  }
72 
73  if (cap_get_flag(caps, cap, CAP_PERMITTED, &cap_state) != 0) {
75  "Failed to check permitted set for "
76  "CAP_SYS_ADMIN (errno: %d)",
77  errno);
78  return false;
79  }
80 
81  if (cap_state != CAP_SET) {
83  "CAP_SYS_ADMIN cannot be obtained. It's "
84  "not in the process's permitted-set.");
85  return false;
86  }
87 
88  if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) != 0) {
90  "Cannot set CAP_SYS_ADMIN as effective "
91  "for the current process (errno: %d)",
92  errno);
93  return false;
94  }
95 
96  if (cap_set_proc(caps) != 0) {
98  "Cannot reset capabilities for current "
99  "process (errno: %d)",
100  errno);
101  return false;
102  }
103 
104  LogCvmfs(kLogUnionFs, kLogDebug, "Successfully obtained CAP_SYS_ADMIN");
105  return true;
106 }
107 
109  cap_t caps = cap_get_proc();
110  const bool result = ObtainSysAdminCapabilityInternal(caps);
111  cap_free(caps);
112  return result;
113 }
114 
117  if (entry->IsGraftMarker() || entry->IsWhiteout() || entry->IsDirectory()) {
118  return;
119  }
120 
121  CheckForBrokenHardlink(entry);
122  MaskFileHardlinks(entry);
123 }
124 
126  SharedPtr<SyncItem> entry) const {
127  if (!entry->IsNew() && !entry->WasDirectory()
128  && entry->GetRdOnlyLinkcount() > 1) {
130  "OverlayFS has copied-up a file (%s) "
131  "with existing hardlinks in lowerdir "
132  "(linkcount %d). OverlayFS cannot handle "
133  "hardlinks and would produce "
134  "inconsistencies. \n\n"
135  "Consider running this command: \n"
136  " cvmfs_server eliminate-hardlinks\n\n"
137  "Aborting...",
138  entry->GetUnionPath().c_str(), entry->GetRdOnlyLinkcount());
139  }
140 }
141 
143  assert(entry->IsRegularFile() || entry->IsSymlink()
144  || entry->IsSpecialFile());
145  if (entry->GetUnionLinkcount() > 1) {
147  "Warning: Found file with linkcount > 1 "
148  "(%s). We will break up these hardlinks.",
149  entry->GetUnionPath().c_str());
150  entry->MaskHardlink();
151  }
152 }
153 
155  assert(this->IsInitialized());
156 
157  FileSystemTraversal<SyncUnionOverlayfs> traversal(this, scratch_path(), true);
158 
169 
171  "OverlayFS starting traversal "
172  "recursion for scratch_path=[%s]",
173  scratch_path().c_str());
174  traversal.Recurse(scratch_path());
175 }
176 
185 bool SyncUnionOverlayfs::ReadlinkEquals(string const &path,
186  string const &compare_value) {
187  char *buf;
188  size_t compare_len;
189 
190  // Compare to one more than compare_value length in case the link value
191  // begins with compare_value but ends with something else
192  compare_len = compare_value.length() + 1;
193 
194  // Allocate enough space for compare_len and terminating null
195  buf = static_cast<char *>(alloca(compare_len + 1));
196 
197  ssize_t len = ::readlink(path.c_str(), buf, compare_len);
198  if (len != -1) {
199  buf[len] = '\0';
200  // have link, return true if it is equal to compare_value
201  return (std::string(buf) == compare_value);
202  } else {
203  // Error, return false
205  "SyncUnionOverlayfs::ReadlinkEquals error reading link [%s]: %d\n",
206  path.c_str(), errno);
207  return false;
208  }
209 }
210 
219 bool SyncUnionOverlayfs::HasXattr(string const &path, string const &attr_name) {
220  // TODO(reneme): it is quite heavy-weight to allocate an object that contains
221  // an std::map<> just to check if an xattr is there...
223  assert(xattrs.IsValid());
224 
225  std::vector<std::string> attrs = xattrs->ListKeys();
226  std::vector<std::string>::const_iterator i = attrs.begin();
227  std::vector<std::string>::const_iterator iend = attrs.end();
228  LogCvmfs(kLogCvmfs, kLogDebug, "Attrs:");
229  for (; i != iend; ++i) {
230  LogCvmfs(kLogCvmfs, kLogDebug, "Attr: %s", i->c_str());
231  }
232 
233  return xattrs.IsValid() && xattrs->Has(attr_name);
234 }
235 
244  bool is_chardev_whiteout = entry->IsCharacterDevice()
245  && entry->GetRdevMajor() == 0
246  && entry->GetRdevMinor() == 0;
247  if (is_chardev_whiteout)
248  return true;
249 
250  std::string whiteout_prefix_ = ".wh.";
251  bool has_wh_prefix = HasPrefix(entry->filename().c_str(), whiteout_prefix_,
252  true);
253  if (has_wh_prefix)
254  return true;
255 
256  bool is_symlink_whiteout = entry->IsSymlink()
257  && IsWhiteoutSymlinkPath(entry->GetScratchPath());
258  if (is_symlink_whiteout)
259  return true;
260 
261  return false;
262 }
263 
264 bool SyncUnionOverlayfs::IsWhiteoutSymlinkPath(const string &path) const {
265  const bool is_whiteout = ReadlinkEquals(path, "(overlay-whiteout)");
266  // TODO(reneme): check for the xattr trusted.overlay.whiteout
267  // Note: This requires CAP_SYS_ADMIN or root... >.<
268  if (is_whiteout) {
269  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] is whiteout symlink",
270  path.c_str());
271  } else {
272  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] is not a whiteout symlink",
273  path.c_str());
274  }
275  return is_whiteout;
276 }
277 
279  SharedPtr<SyncItem> directory) const {
280  const std::string path = directory->GetScratchPath();
281  return DirectoryExists(path) && IsOpaqueDirPath(path);
282 }
283 
284 bool SyncUnionOverlayfs::IsOpaqueDirPath(const string &path) const {
285  bool is_opaque = HasXattr(path.c_str(), "trusted.overlay.opaque");
286  if (is_opaque) {
287  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] has opaque xattr",
288  path.c_str());
289  }
290  return is_opaque;
291 }
292 
294  SharedPtr<SyncItem> entry) const {
295  std::string whiteout_prefix_ = ".wh.";
296 
297  if (HasPrefix(entry->filename().c_str(), whiteout_prefix_, true)) {
298  return entry->filename().substr(whiteout_prefix_.length());
299  } else {
300  return entry->filename();
301  }
302 }
303 } // namespace publish
virtual void LeaveDirectory(const std::string &parent_dir, const std::string &dir_name)
Definition: sync_union.cc:141
bool IsWhiteoutSymlinkPath(const std::string &path) const
void PreprocessSyncItem(SharedPtr< SyncItem > entry) const
VoidCallback fn_new_symlink
Definition: fs_traversal.h:47
VoidCallback fn_new_character_dev
Definition: fs_traversal.h:50
void Recurse(const std::string &dir_path) const
Definition: fs_traversal.h:110
#define PANIC(...)
Definition: exception.h:29
bool IsOpaqueDirectory(SharedPtr< SyncItem > directory) const
virtual bool Initialize()
Definition: sync_union.cc:24
virtual void PreprocessSyncItem(SharedPtr< SyncItem > entry) const
Definition: sync_union.cc:47
std::string scratch_path() const
Definition: sync_union.h:98
static bool HasXattr(std::string const &path, std::string const &attr_name)
A simple recursion engine to abstract the recursion of directories. It provides several callback hook...
Definition: fs_traversal.h:36
assert((mem||(size==0))&&"Out Of Memory")
VoidCallback fn_leave_dir
Definition: fs_traversal.h:45
bool ObtainSysAdminCapabilityInternal(cap_t caps)
virtual void ProcessSymlink(const std::string &parent_dir, const std::string &link_name)
Definition: sync_union.cc:107
BoolCallback fn_ignore_file
Definition: fs_traversal.h:61
std::string UnwindWhiteoutFilename(SharedPtr< SyncItem > entry) const
VoidCallback fn_new_file
Definition: fs_traversal.h:46
VoidCallback fn_enter_dir
Definition: fs_traversal.h:44
bool IsInitialized() const
Definition: sync_union.h:139
void ProcessBlockDevice(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:157
virtual bool IgnoreFilePredicate(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:57
void ProcessSocket(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:175
bool IsOpaqueDirPath(const std::string &path) const
virtual void ProcessRegularFile(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:99
VoidCallback fn_new_block_dev
Definition: fs_traversal.h:49
bool Has(const std::string &key) const
Definition: xattr.cc:103
void MaskFileHardlinks(SharedPtr< SyncItem > entry) const
void CheckForBrokenHardlink(SharedPtr< SyncItem > entry) const
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:279
bool DirectoryExists(const std::string &path)
Definition: posix.cc:824
SyncUnionOverlayfs(SyncMediator *mediator, const std::string &rdonly_path, const std::string &union_path, const std::string &scratch_path)
VoidCallback fn_new_socket
Definition: fs_traversal.h:48
BoolCallback fn_new_dir_prefix
Definition: fs_traversal.h:68
VoidCallback fn_new_fifo
Definition: fs_traversal.h:51
virtual bool ProcessDirectory(const std::string &parent_dir, const std::string &dir_name)
static bool ReadlinkEquals(std::string const &path, std::string const &compare_value)
static XattrList * CreateFromFile(const std::string &path)
Definition: xattr.cc:32
const int kLogVerboseMsg
virtual void EnterDirectory(const std::string &parent_dir, const std::string &dir_name)
Definition: sync_union.cc:135
std::vector< std::string > ListKeys() const
Definition: xattr.cc:119
void ProcessFifo(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:167
void ProcessCharacterDevice(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:147
bool IsWhiteoutEntry(SharedPtr< SyncItem > entry) const
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545