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*/ const cap_value_t cap =
37  CAP_SYS_ADMIN; // is non-const as cap_set_flag()
38  // expects a non-const pointer
39  // on RHEL 5 and older
40 
41 // do sanity-check if supported in <sys/capability.h> otherwise just pray...
42 // Note: CAP_SYS_ADMIN is a rather common capability and is very likely to be
43 // supported by all our target systems. If it is not, one of the next
44 // commands will fail with a less descriptive error message.
45 #ifdef CAP_IS_SUPPORTED
46  if (!CAP_IS_SUPPORTED(cap)) {
47  LogCvmfs(kLogUnionFs, kLogStderr, "System doesn't support CAP_SYS_ADMIN");
48  return false;
49  }
50 #endif
51 
52  if (caps == NULL) {
54  "Failed to obtain capability state "
55  "of current process (errno: %d)",
56  errno);
57  return false;
58  }
59 
60  cap_flag_value_t cap_state;
61  if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &cap_state) != 0) {
63  "Failed to check effective set for "
64  "CAP_SYS_ADMIN (errno: %d)",
65  errno);
66  return false;
67  }
68 
69  if (cap_state == CAP_SET) {
70  LogCvmfs(kLogUnionFs, kLogDebug, "CAP_SYS_ADMIN is already effective");
71  return true;
72  }
73 
74  if (cap_get_flag(caps, cap, CAP_PERMITTED, &cap_state) != 0) {
76  "Failed to check permitted set for "
77  "CAP_SYS_ADMIN (errno: %d)",
78  errno);
79  return false;
80  }
81 
82  if (cap_state != CAP_SET) {
84  "CAP_SYS_ADMIN cannot be obtained. It's "
85  "not in the process's permitted-set.");
86  return false;
87  }
88 
89  if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) != 0) {
91  "Cannot set CAP_SYS_ADMIN as effective "
92  "for the current process (errno: %d)",
93  errno);
94  return false;
95  }
96 
97  if (cap_set_proc(caps) != 0) {
99  "Cannot reset capabilities for current "
100  "process (errno: %d)",
101  errno);
102  return false;
103  }
104 
105  LogCvmfs(kLogUnionFs, kLogDebug, "Successfully obtained CAP_SYS_ADMIN");
106  return true;
107 }
108 
110  cap_t caps = cap_get_proc();
111  const bool result = ObtainSysAdminCapabilityInternal(caps);
112  cap_free(caps);
113  return result;
114 }
115 
118  if (entry->IsGraftMarker() || entry->IsWhiteout() || entry->IsDirectory()) {
119  return;
120  }
121 
122  CheckForBrokenHardlink(entry);
123  MaskFileHardlinks(entry);
124 }
125 
127  SharedPtr<SyncItem> entry) const {
128  if (!entry->IsNew() && !entry->WasDirectory()
129  && entry->GetRdOnlyLinkcount() > 1) {
131  "OverlayFS has copied-up a file (%s) "
132  "with existing hardlinks in lowerdir "
133  "(linkcount %d). OverlayFS cannot handle "
134  "hardlinks and would produce "
135  "inconsistencies. \n\n"
136  "Consider running this command: \n"
137  " cvmfs_server eliminate-hardlinks\n\n"
138  "Aborting...",
139  entry->GetUnionPath().c_str(), entry->GetRdOnlyLinkcount());
140  }
141 }
142 
144  assert(entry->IsRegularFile() || entry->IsSymlink()
145  || entry->IsSpecialFile());
146  if (entry->GetUnionLinkcount() > 1) {
148  "Warning: Found file with linkcount > 1 "
149  "(%s). We will break up these hardlinks.",
150  entry->GetUnionPath().c_str());
151  entry->MaskHardlink();
152  }
153 }
154 
156  assert(this->IsInitialized());
157 
158  FileSystemTraversal<SyncUnionOverlayfs> traversal(this, scratch_path(), true);
159 
170 
172  "OverlayFS starting traversal "
173  "recursion for scratch_path=[%s]",
174  scratch_path().c_str());
175  traversal.Recurse(scratch_path());
176 }
177 
186 bool SyncUnionOverlayfs::ReadlinkEquals(string const &path,
187  string const &compare_value) {
188  char *buf;
189  size_t compare_len;
190 
191  // Compare to one more than compare_value length in case the link value
192  // begins with compare_value but ends with something else
193  compare_len = compare_value.length() + 1;
194 
195  // Allocate enough space for compare_len and terminating null
196  buf = static_cast<char *>(alloca(compare_len + 1));
197 
198  const ssize_t len = ::readlink(path.c_str(), buf, compare_len);
199  if (len != -1) {
200  buf[len] = '\0';
201  // have link, return true if it is equal to compare_value
202  return (std::string(buf) == compare_value);
203  } else {
204  // Error, return false
206  "SyncUnionOverlayfs::ReadlinkEquals error reading link [%s]: %d\n",
207  path.c_str(), errno);
208  return false;
209  }
210 }
211 
220 bool SyncUnionOverlayfs::HasXattr(string const &path, string const &attr_name) {
221  // TODO(reneme): it is quite heavy-weight to allocate an object that contains
222  // an std::map<> just to check if an xattr is there...
224  assert(xattrs.IsValid());
225 
226  std::vector<std::string> attrs = xattrs->ListKeys();
227  std::vector<std::string>::const_iterator i = attrs.begin();
228  const std::vector<std::string>::const_iterator iend = attrs.end();
229  LogCvmfs(kLogCvmfs, kLogDebug, "Attrs:");
230  for (; i != iend; ++i) {
231  LogCvmfs(kLogCvmfs, kLogDebug, "Attr: %s", i->c_str());
232  }
233 
234  return xattrs.IsValid() && xattrs->Has(attr_name);
235 }
236 
245  const bool is_chardev_whiteout = entry->IsCharacterDevice() &&
246  entry->GetRdevMajor() == 0 &&
247  entry->GetRdevMinor() == 0;
248  if (is_chardev_whiteout)
249  return true;
250 
251  const std::string whiteout_prefix_ = ".wh.";
252  const bool has_wh_prefix =
253  HasPrefix(entry->filename().c_str(), whiteout_prefix_, true);
254  if (has_wh_prefix)
255  return true;
256 
257  const bool is_symlink_whiteout =
258  entry->IsSymlink() && IsWhiteoutSymlinkPath(entry->GetScratchPath());
259  if (is_symlink_whiteout)
260  return true;
261 
262  return false;
263 }
264 
265 bool SyncUnionOverlayfs::IsWhiteoutSymlinkPath(const string &path) const {
266  const bool is_whiteout = ReadlinkEquals(path, "(overlay-whiteout)");
267  // TODO(reneme): check for the xattr trusted.overlay.whiteout
268  // Note: This requires CAP_SYS_ADMIN or root... >.<
269  if (is_whiteout) {
270  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] is whiteout symlink",
271  path.c_str());
272  } else {
273  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] is not a whiteout symlink",
274  path.c_str());
275  }
276  return is_whiteout;
277 }
278 
280  SharedPtr<SyncItem> directory) const {
281  const std::string path = directory->GetScratchPath();
282  return DirectoryExists(path) && IsOpaqueDirPath(path);
283 }
284 
285 bool SyncUnionOverlayfs::IsOpaqueDirPath(const string &path) const {
286  const bool is_opaque = HasXattr(path.c_str(), "trusted.overlay.opaque");
287  if (is_opaque) {
288  LogCvmfs(kLogUnionFs, kLogDebug, "OverlayFS [%s] has opaque xattr",
289  path.c_str());
290  }
291  return is_opaque;
292 }
293 
295  SharedPtr<SyncItem> entry) const {
296  const std::string whiteout_prefix_ = ".wh.";
297 
298  if (HasPrefix(entry->filename().c_str(), whiteout_prefix_, true)) {
299  return entry->filename().substr(whiteout_prefix_.length());
300  } else {
301  return entry->filename();
302  }
303 }
304 } // namespace publish
virtual void LeaveDirectory(const std::string &parent_dir, const std::string &dir_name)
Definition: sync_union.cc:144
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:109
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:161
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:180
bool IsOpaqueDirPath(const std::string &path) const
virtual void ProcessRegularFile(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:100
VoidCallback fn_new_block_dev
Definition: fs_traversal.h:49
bool Has(const std::string &key) const
Definition: xattr.cc:104
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:137
std::vector< std::string > ListKeys() const
Definition: xattr.cc:120
void ProcessFifo(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:171
void ProcessCharacterDevice(const std::string &parent_dir, const std::string &filename)
Definition: sync_union.cc:151
bool IsWhiteoutEntry(SharedPtr< SyncItem > entry) const
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545