CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
catalog_virtual.cc
Go to the documentation of this file.
1 
5 #include "catalog_virtual.h"
6 
7 #include <algorithm>
8 #include <cassert>
9 #include <cstdlib>
10 
11 #include "catalog_mgr_rw.h"
13 #include "history.h"
14 #include "swissknife_history.h"
15 #include "swissknife_sync.h"
16 #include "util/logging.h"
17 #include "util/pointer.h"
18 #include "util/posix.h"
19 #include "util/string.h"
20 #include "xattr.h"
21 
22 using namespace std; // NOLINT
23 
24 namespace catalog {
25 
26 const char *VirtualCatalog::kVirtualPath = ".cvmfs";
27 const char *VirtualCatalog::kSnapshotDirectory = "snapshots";
28 const int VirtualCatalog::kActionNone = 0x00;
29 const int VirtualCatalog::kActionGenerateSnapshots = 0x01;
30 const int VirtualCatalog::kActionRemove = 0x02;
31 
32 
33 void VirtualCatalog::CreateBaseDirectory() {
34  // Add /.cvmfs as a nested catalog
35  DirectoryEntryBase entry_dir;
36  entry_dir.name_ = NameString(string(kVirtualPath));
37  entry_dir.mode_ = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
38  | S_IXOTH;
39  entry_dir.uid_ = 0;
40  entry_dir.gid_ = 0;
41  entry_dir.size_ = 97;
42  entry_dir.mtime_ = time(NULL);
43  catalog_mgr_->AddDirectory(entry_dir, XattrList(), "");
45  kVirtualPath);
46  catalog_mgr_->CreateNestedCatalog(kVirtualPath);
48  kVirtualPath);
49  assert(parent_catalog != virtual_catalog);
50 
51  // Set hidden flag in parent catalog
52  DirectoryEntry entry_parent;
53  bool retval = parent_catalog->LookupPath(
54  PathString("/" + string(kVirtualPath)), &entry_parent);
55  assert(retval);
56  entry_parent.set_is_hidden(true);
57  parent_catalog->UpdateEntry(entry_parent, "/" + string(kVirtualPath));
58 
59  // Set hidden flag in nested catalog
60  DirectoryEntry entry_virtual;
61  retval = virtual_catalog->LookupPath(PathString("/" + string(kVirtualPath)),
62  &entry_virtual);
63  assert(retval);
64  entry_virtual.set_is_hidden(true);
65  virtual_catalog->UpdateEntry(entry_virtual, "/" + string(kVirtualPath));
66 }
67 
68 
69 void VirtualCatalog::CreateNestedCatalogMarker() {
70  DirectoryEntryBase entry_marker;
71  // Note that another entity needs to ensure that the object of an empty
72  // file is in the repository! It is currently done by the sync_mediator.
74  catalog_mgr_->spooler_->GetHashAlgorithm();
75  shash::Any file_hash(algorithm);
76  void *empty_compressed;
77  uint64_t sz_empty_compressed;
78  const bool retval =
79  zlib::CompressMem2Mem(NULL, 0, &empty_compressed, &sz_empty_compressed);
80  assert(retval);
81  shash::HashMem(static_cast<unsigned char *>(empty_compressed),
82  sz_empty_compressed, &file_hash);
83  free(empty_compressed);
84  entry_marker.name_ = NameString(".cvmfscatalog");
85  entry_marker.mode_ = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
86  entry_marker.checksum_ = file_hash;
87  entry_marker.mtime_ = time(NULL);
88  entry_marker.uid_ = 0;
89  entry_marker.gid_ = 0;
90  const XattrList xattrs;
91  catalog_mgr_->AddFile(entry_marker, xattrs, kVirtualPath);
92 }
93 
94 
95 void VirtualCatalog::CreateSnapshotDirectory() {
96  DirectoryEntryBase entry_dir;
97  entry_dir.name_ = NameString(string(kSnapshotDirectory));
98  entry_dir.mode_ = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
99  | S_IXOTH;
100  entry_dir.uid_ = 0;
101  entry_dir.gid_ = 0;
102  entry_dir.size_ = 97;
103  entry_dir.mtime_ = time(NULL);
104  catalog_mgr_->AddDirectory(entry_dir, XattrList(), kVirtualPath);
105 }
106 
107 
112 void VirtualCatalog::EnsurePresence() {
113  DirectoryEntry e;
114  const bool retval =
115  catalog_mgr_->LookupPath("/" + string(kVirtualPath), kLookupDefault, &e);
116  if (!retval) {
117  LogCvmfs(kLogCatalog, kLogDebug, "creating new virtual catalog");
118  CreateBaseDirectory();
119  CreateNestedCatalogMarker();
120  CreateSnapshotDirectory();
121  }
122  assert(catalog_mgr_->IsTransitionPoint(kVirtualPath));
123 }
124 
125 
126 void VirtualCatalog::Generate(int actions) {
127  if (actions & kActionGenerateSnapshots) {
128  GenerateSnapshots();
129  }
130  if (actions & kActionRemove) {
131  Remove();
132  }
133 }
134 
135 
136 void VirtualCatalog::GenerateSnapshots() {
137  LogCvmfs(kLogCvmfs, kLogStdout, "Creating virtual snapshots");
138  EnsurePresence();
139 
140  vector<TagId> tags_history;
141  vector<TagId> tags_catalog;
142  GetSortedTagsFromHistory(&tags_history);
143  GetSortedTagsFromCatalog(&tags_catalog);
144  // Add artificial end markers to both lists
145  string tag_name_end = "";
146  if (!tags_history.empty())
147  tag_name_end = std::max(tag_name_end, tags_history.rbegin()->name);
148  if (!tags_catalog.empty())
149  tag_name_end = std::max(tag_name_end, tags_catalog.rbegin()->name);
150  tag_name_end += "X";
151  tags_history.push_back(TagId(tag_name_end, shash::Any()));
152  tags_catalog.push_back(TagId(tag_name_end, shash::Any()));
153 
154  // Walk through both sorted lists concurrently and determine change set
155  unsigned i_history = 0, i_catalog = 0;
156  const unsigned last_history = tags_history.size() - 1;
157  const unsigned last_catalog = tags_catalog.size() - 1;
158  while ((i_history < last_history) || (i_catalog < last_catalog)) {
159  const TagId t_history = tags_history[i_history];
160  const TagId t_catalog = tags_catalog[i_catalog];
161 
162  // Both the same, nothing to do
163  if (t_history == t_catalog) {
164  i_history++;
165  i_catalog++;
166  continue;
167  }
168 
169  // Same tag name for different hash, re-insert
170  if (t_history.name == t_catalog.name) {
171  RemoveSnapshot(t_catalog);
172  InsertSnapshot(t_history);
173  i_history++;
174  i_catalog++;
175  continue;
176  }
177 
178  // New tag that's missing
179  if (t_history.name < t_catalog.name) {
180  InsertSnapshot(t_history);
181  i_history++;
182  continue;
183  }
184 
185  // A tag was removed but it is still present in the catalog
186  assert(t_history.name > t_catalog.name);
187  RemoveSnapshot(t_catalog);
188  i_catalog++;
189  }
190 }
191 
192 
193 bool VirtualCatalog::ParseActions(const string &action_desc, int *actions) {
194  *actions = kActionNone;
195  if (action_desc.empty())
196  return true;
197 
198  vector<string> action_tokens = SplitString(action_desc, ',');
199  for (unsigned i = 0; i < action_tokens.size(); ++i) {
200  if (action_tokens[i] == "snapshots") {
201  *actions |= kActionGenerateSnapshots;
202  } else if (action_tokens[i] == "remove") {
203  *actions |= kActionRemove;
204  } else {
205  return false;
206  }
207  }
208  return true;
209 }
210 
211 
212 void VirtualCatalog::GetSortedTagsFromHistory(vector<TagId> *tags) {
214  assistant_.GetHistory(swissknife::Assistant::kOpenReadOnly));
215  vector<history::History::Tag> tags_history;
216  const bool retval = history->List(&tags_history);
217  assert(retval);
218  for (unsigned i = 0, l = tags_history.size(); i < l; ++i) {
219  if ((tags_history[i].name == swissknife::CommandTag::kHeadTag)
220  || (tags_history[i].name == swissknife::CommandTag::kPreviousHeadTag)) {
221  continue;
222  }
223  tags->push_back(TagId(tags_history[i].name, tags_history[i].root_hash));
224  }
225  std::sort(tags->begin(), tags->end());
226 }
227 
228 
229 void VirtualCatalog::GetSortedTagsFromCatalog(vector<TagId> *tags) {
230  WritableCatalog *virtual_catalog = catalog_mgr_->GetHostingCatalog(
231  kVirtualPath);
232  assert(virtual_catalog != NULL);
233  Catalog::NestedCatalogList nested_catalogs = virtual_catalog
234  ->ListNestedCatalogs();
235  for (unsigned i = 0, l = nested_catalogs.size(); i < l; ++i) {
236  tags->push_back(TagId(GetFileName(nested_catalogs[i].mountpoint).ToString(),
237  nested_catalogs[i].hash));
238  }
239  std::sort(tags->begin(), tags->end());
240 }
241 
242 
243 void VirtualCatalog::InsertSnapshot(TagId tag) {
244  LogCvmfs(kLogCatalog, kLogDebug, "add snapshot %s (%s) to virtual catalog",
245  tag.name.c_str(), tag.hash.ToString().c_str());
246  const UniquePtr<Catalog> catalog(
247  assistant_.GetCatalog(tag.hash, swissknife::Assistant::kOpenReadOnly));
248  assert(catalog.IsValid());
249  assert(catalog->root_prefix().IsEmpty());
250  DirectoryEntry entry_root;
251  const bool retval = catalog->LookupPath(PathString(""), &entry_root);
252  assert(retval);
253 
254  // Add directory entry
255  DirectoryEntryBase entry_dir = entry_root;
256  entry_dir.name_ = NameString(tag.name);
258  entry_dir, XattrList(),
259  string(kVirtualPath) + "/" + string(kSnapshotDirectory));
260 
261  // Set "bind mount" flag
262  WritableCatalog *virtual_catalog = catalog_mgr_->GetHostingCatalog(
263  kVirtualPath);
264  assert(virtual_catalog != NULL);
265  const string mountpoint = "/" + string(kVirtualPath) + "/" +
266  string(kSnapshotDirectory) + "/" + tag.name;
267  DirectoryEntry entry_bind_mountpoint(entry_dir);
268  entry_bind_mountpoint.set_is_bind_mountpoint(true);
269  virtual_catalog->UpdateEntry(entry_bind_mountpoint, mountpoint);
270 
271  // Register nested catalog
272  const uint64_t catalog_size = GetFileSize(catalog->database_path());
273  assert(catalog_size > 0);
274  virtual_catalog->InsertBindMountpoint(mountpoint, tag.hash, catalog_size);
275 }
276 
277 
278 void VirtualCatalog::Remove() {
279  LogCvmfs(kLogCvmfs, kLogStdout, "Removing .cvmfs virtual catalog");
280 
281  // Safety check, make sure we don't remove the entire repository
282  WritableCatalog *virtual_catalog = catalog_mgr_->GetHostingCatalog(
283  kVirtualPath);
284  assert(!virtual_catalog->IsRoot());
285  DirectoryEntry entry_virtual;
286  const bool retval = catalog_mgr_->LookupPath(
287  PathString("/" + string(kVirtualPath)), kLookupDefault, &entry_virtual);
288  assert(retval);
289  assert(entry_virtual.IsHidden());
290 
291  RemoveRecursively(kVirtualPath);
292  catalog_mgr_->RemoveNestedCatalog(kVirtualPath);
293  catalog_mgr_->RemoveDirectory(kVirtualPath);
294 }
295 
296 
297 void VirtualCatalog::RemoveRecursively(const string &directory) {
298  DirectoryEntryList listing;
299  const bool retval =
300  catalog_mgr_->Listing(PathString("/" + directory), &listing);
301  assert(retval);
302  for (unsigned i = 0; i < listing.size(); ++i) {
303  const string this_path = directory + "/" + listing[i].name().ToString();
304  if (listing[i].IsDirectory()) {
305  if (!listing[i].IsBindMountpoint())
306  RemoveRecursively(this_path);
307  catalog_mgr_->RemoveDirectory(this_path);
308  } else if (listing[i].IsRegular()) {
309  assert(listing[i].name().ToString() == ".cvmfscatalog");
310  catalog_mgr_->RemoveFile(this_path);
311  } else {
312  abort();
313  }
314  }
315 }
316 
317 
318 void VirtualCatalog::RemoveSnapshot(TagId tag) {
320  "remove snapshot %s (%s) from virtual catalog", tag.name.c_str(),
321  tag.hash.ToString().c_str());
322  const string tag_dir =
323  string(kVirtualPath) + "/" + string(kSnapshotDirectory) + "/" + tag.name;
324  catalog_mgr_->RemoveDirectory(tag_dir);
325 
326  WritableCatalog *virtual_catalog = catalog_mgr_->GetHostingCatalog(
327  kVirtualPath);
328  assert(virtual_catalog != NULL);
329  virtual_catalog->RemoveBindMountpoint("/" + tag_dir);
330 }
331 
332 
333 VirtualCatalog::VirtualCatalog(manifest::Manifest *m,
336  SyncParameters *p)
337  : catalog_mgr_(c), assistant_(d, m, p->stratum0, p->dir_temp) { }
338 
339 } // namespace catalog
void set_is_bind_mountpoint(const bool val)
bool IsRoot() const
Definition: catalog.h:189
ShortString< kDefaultMaxName, 1 > NameString
Definition: shortstring.h:214
void AddDirectory(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory)
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:28
static const std::string kPreviousHeadTag
Definition: repository.cc:41
void RemoveDirectory(const std::string &directory_path)
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
WritableCatalog * GetHostingCatalog(const std::string &path)
void InsertBindMountpoint(const std::string &mountpoint, const shash::Any content_hash, const uint64_t size)
Definition: catalog_rw.cc:516
const history::History * history() const
gid_t gid_
bool LookupPath(const PathString &path, DirectoryEntry *dirent) const
Definition: catalog.h:124
void RemoveBindMountpoint(const std::string &mountpoint)
Definition: catalog_rw.cc:570
assert((mem||(size==0))&&"Out Of Memory")
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
const NestedCatalogList & ListNestedCatalogs() const
Definition: catalog.cc:620
const unsigned kLookupDefault
Definition: catalog_mgr.h:43
uint64_t size_
catalog::WritableCatalogManager * catalog_mgr_
Definition: repository.h:384
char algorithm
bool IsTransitionPoint(const std::string &mountpoint)
Algorithms
Definition: hash.h:41
bool Listing(const PathString &path, DirectoryEntryList *listing, const bool expand_symlink)
NameString name_
std::vector< DirectoryEntry > DirectoryEntryList
virtual bool List(std::vector< Tag > *tags) const =0
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
static const std::string kHeadTag
Definition: repository.cc:40
time_t mtime_
void RemoveFile(const std::string &file_path)
void set_is_hidden(const bool val)
void RemoveNestedCatalog(const std::string &mountpoint, const bool merge=true)
void HashMem(const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:255
std::vector< NestedCatalog > NestedCatalogList
Definition: catalog.h:204
unsigned int mode_
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:213
int64_t GetFileSize(const std::string &path)
Definition: posix.cc:812
bool CompressMem2Mem(const void *buf, const int64_t size, void **out_buf, uint64_t *out_size)
Definition: compression.cc:754
shash::Any checksum_
void AddFile(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory)
void CreateNestedCatalog(const std::string &mountpoint)
void UpdateEntry(const DirectoryEntry &entry, const shash::Md5 &path_hash)
Definition: catalog_rw.cc:245
uid_t uid_
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545