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