CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
catalog_mgr_rw.cc
Go to the documentation of this file.
1 
5 #define __STDC_FORMAT_MACROS
6 
7 #include "catalog_mgr_rw.h"
8 
9 #include <inttypes.h>
10 #include <unistd.h>
11 
12 #include <cassert>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <string>
16 
17 #include "catalog_balancer.h"
18 #include "catalog_rw.h"
19 #include "manifest.h"
20 #include "statistics.h"
21 #include "upload.h"
22 #include "util/exception.h"
23 #include "util/logging.h"
24 #include "util/posix.h"
25 #include "util/smalloc.h"
26 
27 using namespace std; // NOLINT
28 
29 namespace catalog {
30 
31 WritableCatalogManager::WritableCatalogManager(
32  const shash::Any &base_hash,
33  const std::string &stratum0,
34  const string &dir_temp,
35  upload::Spooler *spooler,
36  download::DownloadManager *download_manager,
37  bool enforce_limits,
38  const unsigned nested_kcatalog_limit,
39  const unsigned root_kcatalog_limit,
40  const unsigned file_mbyte_limit,
41  perf::Statistics *statistics,
42  bool is_balanceable,
43  unsigned max_weight,
44  unsigned min_weight,
45  const std::string &dir_cache)
46  : SimpleCatalogManager(base_hash, stratum0, dir_temp, download_manager,
47  statistics, false, dir_cache, true /* copy to tmpdir */)
48  , spooler_(spooler)
49  , enforce_limits_(enforce_limits)
50  , nested_kcatalog_limit_(nested_kcatalog_limit)
51  , root_kcatalog_limit_(root_kcatalog_limit)
52  , file_mbyte_limit_(file_mbyte_limit)
53  , is_balanceable_(is_balanceable)
54  , max_weight_(max_weight)
55  , min_weight_(min_weight)
56  , balance_weight_(max_weight / 2)
57 {
58  sync_lock_ =
59  reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
60  int retval = pthread_mutex_init(sync_lock_, NULL);
61  assert(retval == 0);
63  reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t)));
64  retval = pthread_mutex_init(catalog_processing_lock_, NULL);
65  assert(retval == 0);
66 }
67 
68 
70  pthread_mutex_destroy(sync_lock_);
71  free(sync_lock_);
72  pthread_mutex_destroy(catalog_processing_lock_);
74 }
75 
76 
87  const PathString &mountpoint,
88  const shash::Any &catalog_hash,
89  Catalog *parent_catalog)
90 {
91  return new WritableCatalog(mountpoint.ToString(),
92  catalog_hash,
93  parent_catalog);
94 }
95 
96 
98  catalog->TakeDatabaseFileOwnership();
99 }
100 
101 
109  const string &dir_temp,
110  const bool volatile_content,
111  const std::string &voms_authz,
112  upload::Spooler *spooler)
113 {
114  // Create a new root catalog at file_path
115  string file_path = dir_temp + "/new_root_catalog";
116 
117  shash::Algorithms hash_algorithm = spooler->GetHashAlgorithm();
118 
119  // A newly created catalog always needs a root entry
120  // we create and configure this here
121  DirectoryEntry root_entry;
123  root_entry.mode_ = 16877;
124  root_entry.size_ = 4096;
125  root_entry.mtime_ = time(NULL);
126  root_entry.uid_ = getuid();
127  root_entry.gid_ = getgid();
128  root_entry.checksum_ = shash::Any(hash_algorithm);
129  root_entry.linkcount_ = 2;
130  string root_path = "";
131 
132  // Create the database schema and the initial root entry
133  {
135  if (!new_clg_db.IsValid() ||
136  !new_clg_db->InsertInitialValues(root_path,
137  volatile_content,
138  voms_authz,
139  root_entry))
140  {
141  LogCvmfs(kLogCatalog, kLogStderr, "creation of catalog '%s' failed",
142  file_path.c_str());
143  return NULL;
144  }
145  }
146 
147  // Compress root catalog;
148  int64_t catalog_size = GetFileSize(file_path);
149  if (catalog_size < 0) {
150  unlink(file_path.c_str());
151  return NULL;
152  }
153  string file_path_compressed = file_path + ".compressed";
154  shash::Any hash_catalog(hash_algorithm, shash::kSuffixCatalog);
155  bool retval = zlib::CompressPath2Path(file_path, file_path_compressed,
156  &hash_catalog);
157  if (!retval) {
158  LogCvmfs(kLogCatalog, kLogStderr, "compression of catalog '%s' failed",
159  file_path.c_str());
160  unlink(file_path.c_str());
161  return NULL;
162  }
163  unlink(file_path.c_str());
164 
165  // Create manifest
166  const string manifest_path = dir_temp + "/manifest";
168  new manifest::Manifest(hash_catalog, catalog_size, "");
169  if (!voms_authz.empty()) {
170  manifest->set_has_alt_catalog_path(true);
171  }
172 
173  // Upload catalog
174  spooler->Upload(file_path_compressed, "data/" + hash_catalog.MakePath());
175  spooler->WaitForUpload();
176  unlink(file_path_compressed.c_str());
177  if (spooler->GetNumberOfErrors() > 0) {
178  LogCvmfs(kLogCatalog, kLogStderr, "failed to commit catalog %s",
179  file_path_compressed.c_str());
180  delete manifest;
181  return NULL;
182  }
183 
184  return manifest;
185 }
186 
187 
199 bool WritableCatalogManager::FindCatalog(const string &path,
200  WritableCatalog **result,
201  DirectoryEntry *dirent) {
202  const PathString ps_path(path);
203 
204  Catalog *best_fit =
206  assert(best_fit != NULL);
207  Catalog *catalog = NULL;
208  bool retval =
209  MountSubtree(ps_path, best_fit, true /* is_listable */, &catalog);
210  if (!retval)
211  return false;
212 
214  if (NULL == dirent) {
215  dirent = &dummy;
216  }
217  bool found = catalog->LookupPath(ps_path, dirent);
218  if (!found || !catalog->IsWritable())
219  return false;
220 
221  *result = static_cast<WritableCatalog *>(catalog);
222  return true;
223 }
224 
225 
227  const std::string &path)
228 {
229  WritableCatalog *result = NULL;
230  bool retval = FindCatalog(MakeRelativePath(path), &result, NULL);
231  if (!retval) return NULL;
232  return result;
233 }
234 
235 
241 void WritableCatalogManager::RemoveFile(const std::string &path) {
242  const string file_path = MakeRelativePath(path);
243  const string parent_path = GetParentPath(file_path);
244 
245  SyncLock();
246  WritableCatalog *catalog;
247  if (!FindCatalog(parent_path, &catalog)) {
248  PANIC(kLogStderr, "catalog for file '%s' cannot be found",
249  file_path.c_str());
250  }
251 
252  catalog->RemoveEntry(file_path);
253  SyncUnlock();
254 }
255 
256 
262 void WritableCatalogManager::RemoveDirectory(const std::string &path) {
263  const string directory_path = MakeRelativePath(path);
264  const string parent_path = GetParentPath(directory_path);
265 
266  SyncLock();
267  WritableCatalog *catalog;
268  DirectoryEntry parent_entry;
269  if (!FindCatalog(parent_path, &catalog, &parent_entry)) {
270  PANIC(kLogStderr, "catalog for directory '%s' cannot be found",
271  directory_path.c_str());
272  }
273 
274  parent_entry.set_linkcount(parent_entry.linkcount() - 1);
275 
276  catalog->RemoveEntry(directory_path);
277  catalog->UpdateEntry(parent_entry, parent_path);
278  if (parent_entry.IsNestedCatalogRoot()) {
279  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating transition point %s",
280  parent_path.c_str());
281  WritableCatalog *parent_catalog =
282  reinterpret_cast<WritableCatalog *>(catalog->parent());
283  parent_entry.set_is_nested_catalog_mountpoint(true);
284  parent_entry.set_is_nested_catalog_root(false);
285  parent_catalog->UpdateEntry(parent_entry, parent_path);
286  }
287  SyncUnlock();
288 }
289 
298 void WritableCatalogManager::Clone(const std::string destination,
299  const std::string source) {
300  const std::string relative_source = MakeRelativePath(source);
301 
302  DirectoryEntry source_dirent;
303  if (!LookupPath(relative_source, kLookupDefault, &source_dirent)) {
304  PANIC(kLogStderr, "catalog for file '%s' cannot be found, aborting",
305  source.c_str());
306  }
307  if (source_dirent.IsDirectory()) {
308  PANIC(kLogStderr, "Trying to clone a directory: '%s', aborting",
309  source.c_str());
310  }
311 
312  // if the file is already there we remove it and we add it back
313  DirectoryEntry check_dirent;
314  bool destination_already_present =
315  LookupPath(MakeRelativePath(destination), kLookupDefault, &check_dirent);
316  if (destination_already_present) {
317  this->RemoveFile(destination);
318  }
319 
320  DirectoryEntry destination_dirent(source_dirent);
321  std::string destination_dirname;
322  std::string destination_filename;
323  SplitPath(destination, &destination_dirname, &destination_filename);
324 
325  destination_dirent.name_.Assign(
326  NameString(destination_filename.c_str(), destination_filename.length()));
327 
328  // TODO(jblomer): clone is used by tarball engine and should eventually
329  // support extended attributes
330  this->AddFile(destination_dirent, empty_xattrs, destination_dirname);
331 }
332 
333 
340 void WritableCatalogManager::CloneTree(const std::string &from_dir,
341  const std::string &to_dir)
342 {
343  // Sanitize input paths
344  if (from_dir.empty() || to_dir.empty())
345  PANIC(kLogStderr, "clone tree from or to root impossible");
346 
347  const std::string relative_source = MakeRelativePath(from_dir);
348  const std::string relative_dest = MakeRelativePath(to_dir);
349 
350  if (relative_source == relative_dest) {
351  PANIC(kLogStderr, "cannot clone tree into itself ('%s')", to_dir.c_str());
352  }
353  if (HasPrefix(relative_dest, relative_source + "/", false /*ignore_case*/)) {
355  "cannot clone tree into sub directory of source '%s' --> '%s'",
356  from_dir.c_str(), to_dir.c_str());
357  }
358 
359  DirectoryEntry source_dirent;
360  if (!LookupPath(relative_source, kLookupDefault, &source_dirent)) {
361  PANIC(kLogStderr, "path '%s' cannot be found, aborting", from_dir.c_str());
362  }
363  if (!source_dirent.IsDirectory()) {
364  PANIC(kLogStderr, "CloneTree: source '%s' not a directory, aborting",
365  from_dir.c_str());
366  }
367 
368  DirectoryEntry dest_dirent;
369  if (LookupPath(relative_dest, kLookupDefault, &dest_dirent)) {
370  PANIC(kLogStderr, "destination '%s' exists, aborting", to_dir.c_str());
371  }
372 
373  const std::string dest_parent = GetParentPath(relative_dest);
374  DirectoryEntry dest_parent_dirent;
375  if (!LookupPath(dest_parent, kLookupDefault, &dest_parent_dirent)) {
376  PANIC(kLogStderr, "destination '%s' not on a known path, aborting",
377  to_dir.c_str());
378  }
379 
380  CloneTreeImpl(PathString(from_dir),
381  GetParentPath(to_dir),
382  NameString(GetFileName(to_dir)));
383 }
384 
385 
391  const PathString &source_dir,
392  const std::string &dest_parent_dir,
393  const NameString &dest_name)
394 {
395  LogCvmfs(kLogCatalog, kLogDebug, "cloning %s --> %s/%s", source_dir.c_str(),
396  dest_parent_dir.c_str(), dest_name.ToString().c_str());
397  PathString relative_source(MakeRelativePath(source_dir.ToString()));
398 
399  DirectoryEntry source_dirent;
400  bool retval = LookupPath(relative_source, kLookupDefault, &source_dirent);
401  assert(retval);
402  assert(!source_dirent.IsBindMountpoint());
403 
404  DirectoryEntry dest_dirent(source_dirent);
405  dest_dirent.name_.Assign(dest_name);
406  // Just in case, reset the nested catalog markers
407  dest_dirent.set_is_nested_catalog_mountpoint(false);
408  dest_dirent.set_is_nested_catalog_root(false);
409 
410  XattrList xattrs;
411  if (source_dirent.HasXattrs()) {
412  retval = LookupXattrs(relative_source, &xattrs);
413  assert(retval);
414  }
415  AddDirectory(dest_dirent, xattrs, dest_parent_dir);
416 
417  std::string dest_dir = dest_parent_dir;
418  if (!dest_dir.empty())
419  dest_dir.push_back('/');
420  dest_dir += dest_name.ToString();
421  if (source_dirent.IsNestedCatalogRoot() ||
422  source_dirent.IsNestedCatalogMountpoint())
423  {
424  CreateNestedCatalog(dest_dir);
425  }
426 
428  retval = Listing(relative_source, &ls, false /* expand_symlink */);
429  assert(retval);
430  for (unsigned i = 0; i < ls.size(); ++i) {
431  PathString sub_path(source_dir);
432  assert(!sub_path.IsEmpty());
433  sub_path.Append("/", 1);
434  sub_path.Append(ls[i].name().GetChars(), ls[i].name().GetLength());
435 
436  if (ls[i].IsDirectory()) {
437  CloneTreeImpl(sub_path, dest_dir, ls[i].name());
438  continue;
439  }
440 
441  // We break hard-links during cloning
442  ls[i].set_hardlink_group(0);
443  ls[i].set_linkcount(1);
444 
445  xattrs.Clear();
446  if (ls[i].HasXattrs()) {
447  retval = LookupXattrs(sub_path, &xattrs);
448  assert(retval);
449  }
450 
451  if (ls[i].IsChunkedFile()) {
452  FileChunkList chunks;
453  std::string relative_sub_path = MakeRelativePath(sub_path.ToString());
454  retval = ListFileChunks(
455  PathString(relative_sub_path), ls[i].hash_algorithm(), &chunks);
456  assert(retval);
457  AddChunkedFile(ls[i], xattrs, dest_dir, chunks);
458  } else {
459  AddFile(ls[i], xattrs, dest_dir);
460  }
461  }
462 }
463 
464 
473  const XattrList &xattrs,
474  const std::string &parent_directory)
475 {
476  const string parent_path = MakeRelativePath(parent_directory);
477  string directory_path = parent_path + "/";
478  directory_path.append(entry.name().GetChars(), entry.name().GetLength());
479 
480  SyncLock();
481  WritableCatalog *catalog;
482  DirectoryEntry parent_entry;
483  if (!FindCatalog(parent_path, &catalog, &parent_entry)) {
484  PANIC(kLogStderr, "catalog for directory '%s' cannot be found",
485  directory_path.c_str());
486  }
487 
488  DirectoryEntry fixed_hardlink_count(entry);
489  fixed_hardlink_count.set_linkcount(2);
490  catalog->AddEntry(fixed_hardlink_count, xattrs,
491  directory_path, parent_path);
492 
493  parent_entry.set_linkcount(parent_entry.linkcount() + 1);
494  catalog->UpdateEntry(parent_entry, parent_path);
495  if (parent_entry.IsNestedCatalogRoot()) {
496  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating transition point %s",
497  parent_path.c_str());
498  WritableCatalog *parent_catalog =
499  reinterpret_cast<WritableCatalog *>(catalog->parent());
500  parent_entry.set_is_nested_catalog_mountpoint(true);
501  parent_entry.set_is_nested_catalog_root(false);
502  parent_catalog->UpdateEntry(parent_entry, parent_path);
503  }
504  SyncUnlock();
505 }
506 
515  const DirectoryEntry &entry,
516  const XattrList &xattrs,
517  const std::string &parent_directory)
518 {
519  const string parent_path = MakeRelativePath(parent_directory);
520  const string file_path = entry.GetFullPath(parent_path);
521 
522  SyncLock();
523  WritableCatalog *catalog;
524  if (!FindCatalog(parent_path, &catalog)) {
525  PANIC(kLogStderr, "catalog for file '%s' cannot be found",
526  file_path.c_str());
527  }
528 
529  assert(!entry.IsRegular() || entry.IsChunkedFile() ||
530  !entry.checksum().IsNull());
531  assert(entry.IsRegular() || !entry.IsExternalFile());
532 
533  // check if file is too big
534  unsigned mbytes = entry.size() / (1024 * 1024);
535  if ((file_mbyte_limit_ > 0) && (mbytes > file_mbyte_limit_)) {
537  "%s: file at %s is larger than %u megabytes (%u). "
538  "CernVM-FS works best with small files. "
539  "Please remove the file or increase the limit.",
540  enforce_limits_ ? "FATAL" : "WARNING", file_path.c_str(),
541  file_mbyte_limit_, mbytes);
542  if (enforce_limits_)
543  PANIC(kLogStderr, "file at %s is larger than %u megabytes (%u).",
544  file_path.c_str(), file_mbyte_limit_, mbytes);
545  }
546 
547  catalog->AddEntry(entry, xattrs, file_path, parent_path);
548  SyncUnlock();
549 }
550 
551 
553  const DirectoryEntryBase &entry,
554  const XattrList &xattrs,
555  const std::string &parent_directory,
556  const FileChunkList &file_chunks)
557 {
558  assert(file_chunks.size() > 0);
559 
560  DirectoryEntry full_entry(entry);
561  full_entry.set_is_chunked_file(true);
562 
563  AddFile(full_entry, xattrs, parent_directory);
564 
565  const string parent_path = MakeRelativePath(parent_directory);
566  const string file_path = entry.GetFullPath(parent_path);
567 
568  SyncLock();
569  WritableCatalog *catalog;
570  if (!FindCatalog(parent_path, &catalog)) {
571  PANIC(kLogStderr, "catalog for file '%s' cannot be found",
572  file_path.c_str());
573  }
574 
575  for (unsigned i = 0; i < file_chunks.size(); ++i) {
576  catalog->AddFileChunk(file_path, *file_chunks.AtPtr(i));
577  }
578  SyncUnlock();
579 }
580 
581 
590  const DirectoryEntryBaseList &entries,
591  const XattrList &xattrs,
592  const std::string &parent_directory,
593  const FileChunkList &file_chunks)
594 {
595  assert(entries.size() >= 1);
596  assert(file_chunks.IsEmpty() || entries[0].IsRegular());
597  if (entries.size() == 1) {
598  DirectoryEntry fix_linkcount(entries[0]);
599  fix_linkcount.set_linkcount(1);
600  if (file_chunks.IsEmpty())
601  return AddFile(fix_linkcount, xattrs, parent_directory);
602  return AddChunkedFile(fix_linkcount, xattrs, parent_directory, file_chunks);
603  }
604 
605  LogCvmfs(kLogCatalog, kLogVerboseMsg, "adding hardlink group %s/%s",
606  parent_directory.c_str(), entries[0].name().c_str());
607 
608  // Hardlink groups have to reside in the same directory.
609  // Therefore we only have one parent directory here
610  const string parent_path = MakeRelativePath(parent_directory);
611 
612  // check if hard link is too big
613  unsigned mbytes = entries[0].size() / (1024 * 1024);
614  if ((file_mbyte_limit_ > 0) && (mbytes > file_mbyte_limit_)) {
616  "%s: hard link at %s is larger than %u megabytes (%u). "
617  "CernVM-FS works best with small files. "
618  "Please remove the file or increase the limit.",
619  enforce_limits_ ? "FATAL" : "WARNING",
620  (parent_path + entries[0].name().ToString()).c_str(),
622  mbytes);
623  if (enforce_limits_)
624  PANIC(kLogStderr, "hard link at %s is larger than %u megabytes (%u)",
625  (parent_path + entries[0].name().ToString()).c_str(),
626  file_mbyte_limit_, mbytes);
627  }
628 
629  SyncLock();
630  WritableCatalog *catalog;
631  if (!FindCatalog(parent_path, &catalog)) {
633  "catalog for hardlink group containing '%s' cannot be found",
634  parent_path.c_str());
635  }
636 
637  // Get a valid hardlink group id for the catalog the group will end up in
638  // TODO(unknown): Compaction
639  uint32_t new_group_id = catalog->GetMaxLinkId() + 1;
640  LogCvmfs(kLogCatalog, kLogVerboseMsg, "hardlink group id %u issued",
641  new_group_id);
642  assert(new_group_id > 0);
643 
644  // Add the file entries to the catalog
645  for (DirectoryEntryBaseList::const_iterator i = entries.begin(),
646  iEnd = entries.end(); i != iEnd; ++i)
647  {
648  string file_path = parent_path + "/";
649  file_path.append(i->name().GetChars(), i->name().GetLength());
650 
651  // create a fully fledged DirectoryEntry to add the hardlink group to it
652  // which is CVMFS specific meta data.
653  DirectoryEntry hardlink(*i);
654  hardlink.set_hardlink_group(new_group_id);
655  hardlink.set_linkcount(entries.size());
656  hardlink.set_is_chunked_file(!file_chunks.IsEmpty());
657 
658  catalog->AddEntry(hardlink, xattrs, file_path, parent_path);
659  if (hardlink.IsChunkedFile()) {
660  for (unsigned i = 0; i < file_chunks.size(); ++i) {
661  catalog->AddFileChunk(file_path, *file_chunks.AtPtr(i));
662  }
663  }
664  }
665  SyncUnlock();
666 }
667 
668 
669 void WritableCatalogManager::ShrinkHardlinkGroup(const string &remove_path) {
670  const string relative_path = MakeRelativePath(remove_path);
671 
672  SyncLock();
673  WritableCatalog *catalog;
674  if (!FindCatalog(relative_path, &catalog)) {
676  "catalog for hardlink group containing '%s' cannot be found",
677  remove_path.c_str());
678  }
679 
680  catalog->IncLinkcount(relative_path, -1);
681  SyncUnlock();
682 }
683 
684 
694  const XattrList &xattrs,
695  const std::string &directory_path)
696 {
697  assert(entry.IsDirectory());
698 
699  const string entry_path = MakeRelativePath(directory_path);
700  const string parent_path = GetParentPath(entry_path);
701 
702  SyncLock();
703  // find the catalog to be updated
704  WritableCatalog *catalog;
705  if (!FindCatalog(parent_path, &catalog)) {
706  PANIC(kLogStderr, "catalog for entry '%s' cannot be found",
707  entry_path.c_str());
708  }
709 
710  catalog->TouchEntry(entry, xattrs, entry_path);
711 
712  // since we deal with a directory here, we might just touch a
713  // nested catalog transition point. If this is the case we would need to
714  // update two catalog entries:
715  // * the nested catalog MOUNTPOINT in the parent catalog
716  // * the nested catalog ROOT in the nested catalog
717 
718  // first check if we really have a nested catalog transition point
719  catalog::DirectoryEntry potential_transition_point;
720  PathString transition_path(entry_path.data(), entry_path.length());
721  bool retval = catalog->LookupPath(transition_path,
722  &potential_transition_point);
723  assert(retval);
724  if (potential_transition_point.IsNestedCatalogMountpoint()) {
726  "updating transition point at %s", entry_path.c_str());
727 
728  // find and mount nested catalog associated to this transition point
729  shash::Any nested_hash;
730  uint64_t nested_size;
731  retval = catalog->FindNested(transition_path, &nested_hash, &nested_size);
732  assert(retval);
733  Catalog *nested_catalog;
734  nested_catalog = MountCatalog(transition_path, nested_hash, catalog);
735  assert(nested_catalog != NULL);
736 
737  // update nested catalog root in the child catalog
738  reinterpret_cast<WritableCatalog *>(nested_catalog)->
739  TouchEntry(entry, xattrs, entry_path);
740  }
741 
742  SyncUnlock();
743 }
744 
745 
752 void WritableCatalogManager::CreateNestedCatalog(const std::string &mountpoint)
753 {
754  const string nested_root_path = MakeRelativePath(mountpoint);
755  const PathString ps_nested_root_path(nested_root_path);
756 
757  SyncLock();
758  // Find the catalog currently containing the directory structure, which
759  // will be represented as a new nested catalog and its root-entry/mountpoint
760  // along the way
761  WritableCatalog *old_catalog = NULL;
762  DirectoryEntry new_root_entry;
763  if (!FindCatalog(nested_root_path, &old_catalog, &new_root_entry)) {
765  "failed to create nested catalog '%s': "
766  "mountpoint was not found in current catalog structure",
767  nested_root_path.c_str());
768  }
769 
770  // Create the database schema and the initial root entry
771  // for the new nested catalog
772  const string database_file_path = CreateTempPath(dir_temp() + "/catalog",
773  0666);
774  const bool volatile_content = false;
775  CatalogDatabase *new_catalog_db = CatalogDatabase::Create(database_file_path);
776  assert(NULL != new_catalog_db);
777  // Note we do not set the external_data bit for nested catalogs
778  bool retval =
779  new_catalog_db->InsertInitialValues(nested_root_path,
780  volatile_content,
781  "", // At this point, only root
782  // catalog gets VOMS authz
783  new_root_entry);
784  assert(retval);
785  // TODO(rmeusel): we need a way to attach a catalog directly from an open
786  // database to remove this indirection
787  delete new_catalog_db;
788  new_catalog_db = NULL;
789 
790  // Attach the just created nested catalog
791  Catalog *new_catalog =
792  CreateCatalog(ps_nested_root_path, shash::Any(), old_catalog);
793  retval = AttachCatalog(database_file_path, new_catalog);
794  assert(retval);
795 
796  assert(new_catalog->IsWritable());
797  WritableCatalog *wr_new_catalog = static_cast<WritableCatalog *>(new_catalog);
798 
799  if (new_root_entry.HasXattrs()) {
800  XattrList xattrs;
801  retval = old_catalog->LookupXattrsPath(ps_nested_root_path, &xattrs);
802  assert(retval);
803  wr_new_catalog->TouchEntry(new_root_entry, xattrs, nested_root_path);
804  }
805 
806  // From now on, there are two catalogs, spanning the same directory structure
807  // we have to split the overlapping directory entries from the old catalog
808  // to the new catalog to re-gain a valid catalog structure
809  old_catalog->Partition(wr_new_catalog);
810 
811  // Add the newly created nested catalog to the references of the containing
812  // catalog
813  old_catalog->InsertNestedCatalog(new_catalog->mountpoint().ToString(), NULL,
814  shash::Any(spooler_->GetHashAlgorithm()), 0);
815 
816  // Fix subtree counters in new nested catalogs: subtree is the sum of all
817  // entries of all "grand-nested" catalogs
818  // Note: taking a copy of the nested catalog list here
819  const Catalog::NestedCatalogList &grand_nested =
820  wr_new_catalog->ListOwnNestedCatalogs();
821  DeltaCounters fix_subtree_counters;
822  for (Catalog::NestedCatalogList::const_iterator i = grand_nested.begin(),
823  iEnd = grand_nested.end(); i != iEnd; ++i)
824  {
825  WritableCatalog *grand_catalog;
826  retval = FindCatalog(i->mountpoint.ToString(), &grand_catalog);
827  assert(retval);
828  const Counters &grand_counters = grand_catalog->GetCounters();
829  grand_counters.AddAsSubtree(&fix_subtree_counters);
830  }
831  DeltaCounters save_counters = wr_new_catalog->delta_counters_;
832  wr_new_catalog->delta_counters_ = fix_subtree_counters;
833  wr_new_catalog->UpdateCounters();
834  wr_new_catalog->delta_counters_ = save_counters;
835 
836  SyncUnlock();
837 }
838 
839 
851 void WritableCatalogManager::RemoveNestedCatalog(const string &mountpoint,
852  const bool merge) {
853  const string nested_root_path = MakeRelativePath(mountpoint);
854 
855  SyncLock();
856  // Find the catalog which should be removed
857  WritableCatalog *nested_catalog = NULL;
858  if (!FindCatalog(nested_root_path, &nested_catalog)) {
860  "failed to remove nested catalog '%s': "
861  "mountpoint was not found in current catalog structure",
862  nested_root_path.c_str());
863  }
864 
865  // Check if the found catalog is really the nested catalog to be deleted
866  assert(!nested_catalog->IsRoot() &&
867  (nested_catalog->mountpoint().ToString() == nested_root_path));
868 
869  if (merge) {
870  // Merge all data from the nested catalog into it's parent
871  nested_catalog->MergeIntoParent();
872  } else {
873  nested_catalog->RemoveFromParent();
874  }
875 
876  // Delete the catalog database file from the working copy
877  if (unlink(nested_catalog->database_path().c_str()) != 0) {
879  "unable to delete the removed nested catalog database file '%s'",
880  nested_catalog->database_path().c_str());
881  }
882 
883  // Remove the catalog from internal data structures
884  DetachCatalog(nested_catalog);
885  SyncUnlock();
886 }
887 
888 
900 void WritableCatalogManager::SwapNestedCatalog(const string &mountpoint,
901  const shash::Any &new_hash,
902  const uint64_t new_size) {
903  const string nested_root_path = MakeRelativePath(mountpoint);
904  const string parent_path = GetParentPath(nested_root_path);
905  const PathString nested_root_ps = PathString(nested_root_path);
906 
907  SyncLock();
908 
909  // Find the immediate parent catalog
910  WritableCatalog *parent = NULL;
911  if (!FindCatalog(parent_path, &parent)) {
912  SyncUnlock(); // this is needed for the unittest. otherwise they get stuck
914  "failed to swap nested catalog '%s': could not find parent '%s'",
915  nested_root_path.c_str(), parent_path.c_str());
916  }
917 
918  // Get old nested catalog counters
919  Catalog *old_attached_catalog = parent->FindChild(nested_root_ps);
920  Counters old_counters;
921  if (old_attached_catalog) {
922  // Old catalog was already attached (e.g. as a child catalog
923  // attached by a prior call to CreateNestedCatalog()). Ensure
924  // that it has not been modified, get counters, and detach it.
925  WritableCatalogList list;
926  if (GetModifiedCatalogLeafsRecursively(old_attached_catalog, &list)) {
927  SyncUnlock();
929  "failed to swap nested catalog '%s': already modified",
930  nested_root_path.c_str());
931  }
932  old_counters = old_attached_catalog->GetCounters();
933  DetachSubtree(old_attached_catalog);
934 
935  } else {
936  // Old catalog was not attached. Download a freely attached
937  // version and get counters.
938  shash::Any old_hash;
939  uint64_t old_size;
940  const bool old_found = parent->FindNested(nested_root_ps, &old_hash,
941  &old_size);
942  if (!old_found) {
943  SyncUnlock();
945  "failed to swap nested catalog '%s': not found in parent",
946  nested_root_path.c_str());
947  }
948  UniquePtr<Catalog> old_free_catalog(
949  LoadFreeCatalog(nested_root_ps, old_hash));
950  if (!old_free_catalog.IsValid()) {
951  SyncUnlock();
953  "failed to swap nested catalog '%s': failed to load old catalog",
954  nested_root_path.c_str());
955  }
956  old_counters = old_free_catalog->GetCounters();
957  }
958 
959  // Load freely attached new catalog
960  UniquePtr<Catalog> new_catalog(LoadFreeCatalog(nested_root_ps, new_hash));
961  if (!new_catalog.IsValid()) {
962  SyncUnlock();
964  "failed to swap nested catalog '%s': failed to load new catalog",
965  nested_root_path.c_str());
966  }
967 
968  // Get new catalog root directory entry
969  DirectoryEntry dirent;
970  XattrList xattrs;
971  const bool dirent_found = new_catalog->LookupPath(nested_root_ps, &dirent);
972  if (!dirent_found) {
973  SyncUnlock();
975  "failed to swap nested catalog '%s': missing dirent in new catalog",
976  nested_root_path.c_str());
977  }
978  if (dirent.HasXattrs()) {
979  const bool xattrs_found = new_catalog->LookupXattrsPath(nested_root_ps,
980  &xattrs);
981  if (!xattrs_found) {
982  SyncUnlock();
984  "failed to swap nested catalog '%s': missing xattrs in new catalog",
985  nested_root_path.c_str());
986  }
987  }
988 
989  // Swap catalogs
990  parent->RemoveNestedCatalog(nested_root_path, NULL);
991  parent->InsertNestedCatalog(nested_root_path, NULL, new_hash, new_size);
992 
993  // Update parent directory entry
995  dirent.set_is_nested_catalog_root(false);
996  parent->UpdateEntry(dirent, nested_root_path);
997  parent->TouchEntry(dirent, xattrs, nested_root_path);
998 
999  // Update counters
1000  DeltaCounters delta = Counters::Diff(old_counters,
1001  new_catalog->GetCounters());
1002  delta.PopulateToParent(&parent->delta_counters_);
1003 
1004  SyncUnlock();
1005 }
1006 
1019 void WritableCatalogManager::GraftNestedCatalog(const string &mountpoint,
1020  const shash::Any &new_hash,
1021  const uint64_t new_size)
1022 {
1023  const string nested_root_path = MakeRelativePath(mountpoint);
1024  const string parent_path = GetParentPath(nested_root_path);
1025  const PathString nested_root_ps = PathString(nested_root_path);
1026 
1027  assert(!nested_root_path.empty());
1028 
1029  // Load freely attached new catalog
1030  UniquePtr<Catalog> new_catalog(LoadFreeCatalog(nested_root_ps, new_hash));
1031  if (!new_catalog.IsValid()) {
1032  PANIC(kLogStderr,
1033  "failed to graft nested catalog '%s': failed to load new catalog",
1034  nested_root_path.c_str());
1035  }
1036  if (new_catalog->root_prefix() != nested_root_ps) {
1037  PANIC(kLogStderr,
1038  "invalid nested catalog for grafting at '%s': catalog rooted at '%s'",
1039  nested_root_path.c_str(),
1040  new_catalog->root_prefix().ToString().c_str());
1041  }
1042 
1043  // Get new catalog root directory entry
1044  DirectoryEntry dirent;
1045  XattrList xattrs;
1046  const bool dirent_found = new_catalog->LookupPath(nested_root_ps, &dirent);
1047  if (!dirent_found) {
1048  PANIC(kLogStderr,
1049  "failed to swap nested catalog '%s': missing dirent in new catalog",
1050  nested_root_path.c_str());
1051  }
1052  if (dirent.HasXattrs()) {
1053  const bool xattrs_found = new_catalog->LookupXattrsPath(nested_root_ps,
1054  &xattrs);
1055  if (!xattrs_found) {
1056  PANIC(kLogStderr,
1057  "failed to swap nested catalog '%s': missing xattrs in new catalog",
1058  nested_root_path.c_str());
1059  }
1060  }
1061  // Transform the nested catalog root into a transition point to be inserted
1062  // in the parent catalog
1063  dirent.set_is_nested_catalog_root(false);
1064  dirent.set_is_nested_catalog_mountpoint(true);
1065 
1066  // Add directory and nested catalog
1067 
1068  SyncLock();
1069  WritableCatalog *parent_catalog;
1070  DirectoryEntry parent_entry;
1071  if (!FindCatalog(parent_path, &parent_catalog, &parent_entry)) {
1072  SyncUnlock();
1073  PANIC(kLogStderr, "catalog for directory '%s' cannot be found",
1074  parent_path.c_str());
1075  }
1076  if (parent_catalog->LookupPath(nested_root_ps, NULL)) {
1077  SyncUnlock();
1078  PANIC(kLogStderr, "invalid attempt to graft nested catalog into existing "
1079  "directory '%s'", nested_root_path.c_str());
1080  }
1081  parent_catalog->AddEntry(dirent, xattrs, nested_root_path, parent_path);
1082  parent_entry.set_linkcount(parent_entry.linkcount() + 1);
1083  parent_catalog->UpdateEntry(parent_entry, parent_path);
1084  if (parent_entry.IsNestedCatalogRoot()) {
1085  WritableCatalog *grand_parent_catalog =
1086  reinterpret_cast<WritableCatalog *>(parent_catalog->parent());
1087  parent_entry.set_is_nested_catalog_root(false);
1088  parent_entry.set_is_nested_catalog_mountpoint(true);
1089  grand_parent_catalog->UpdateEntry(parent_entry, parent_path);
1090  }
1091 
1092  parent_catalog->InsertNestedCatalog(
1093  nested_root_path, NULL, new_hash, new_size);
1094 
1095  // Fix-up counters
1096  Counters counters;
1097  DeltaCounters delta = Counters::Diff(counters, new_catalog->GetCounters());
1098  delta.PopulateToParent(&parent_catalog->delta_counters_);
1099 
1100  SyncUnlock();
1101 }
1102 
1106 bool WritableCatalogManager::IsTransitionPoint(const string &mountpoint) {
1107  const string path = MakeRelativePath(mountpoint);
1108 
1109  SyncLock();
1110  WritableCatalog *catalog;
1111  DirectoryEntry entry;
1112  if (!FindCatalog(path, &catalog, &entry)) {
1113  PANIC(kLogStderr, "catalog for directory '%s' cannot be found",
1114  path.c_str());
1115  }
1116  const bool result = entry.IsNestedCatalogRoot();
1117  SyncUnlock();
1118  return result;
1119 }
1120 
1121 
1123  // TODO(jblomer): meant for micro catalogs
1124 }
1125 
1126 
1127 void WritableCatalogManager::SetTTL(const uint64_t new_ttl) {
1128  SyncLock();
1129  reinterpret_cast<WritableCatalog *>(GetRootCatalog())->SetTTL(new_ttl);
1130  SyncUnlock();
1131 }
1132 
1133 
1134 bool WritableCatalogManager::SetVOMSAuthz(const std::string &voms_authz) {
1135  bool result;
1136  SyncLock();
1137  result = reinterpret_cast<WritableCatalog *>(
1138  GetRootCatalog())->SetVOMSAuthz(voms_authz);
1139  SyncUnlock();
1140  return result;
1141 }
1142 
1143 
1144 bool WritableCatalogManager::Commit(const bool stop_for_tweaks,
1145  const uint64_t manual_revision,
1147  WritableCatalog *root_catalog =
1148  reinterpret_cast<WritableCatalog *>(GetRootCatalog());
1149  root_catalog->SetDirty();
1150 
1151  // set root catalog revision to manually provided number if available
1152  if (manual_revision > 0) {
1153  const uint64_t revision = root_catalog->GetRevision();
1154  if (revision >= manual_revision) {
1156  "Manual revision (%" PRIu64 ") must not be "
1157  "smaller than the current root catalog's (%" PRIu64
1158  "). Skipped!", manual_revision, revision);
1159  } else {
1160  // Gets incremented by FinalizeCatalog() afterwards!
1161  root_catalog->SetRevision(manual_revision - 1);
1162  }
1163  }
1164 
1165  // do the actual catalog snapshotting and upload
1166  CatalogInfo root_catalog_info;
1167  if (getenv("_CVMFS_SERIALIZED_CATALOG_PROCESSING_") == NULL)
1168  root_catalog_info = SnapshotCatalogs(stop_for_tweaks);
1169  else
1170  root_catalog_info = SnapshotCatalogsSerialized(stop_for_tweaks);
1171  if (spooler_->GetNumberOfErrors() > 0) {
1172  LogCvmfs(kLogCatalog, kLogStderr, "failed to commit catalogs");
1173  return false;
1174  }
1175 
1176  // .cvmfspublished export
1177  LogCvmfs(kLogCatalog, kLogVerboseMsg, "Committing repository manifest");
1178  set_base_hash(root_catalog_info.content_hash);
1179 
1180  manifest->set_catalog_hash(root_catalog_info.content_hash);
1181  manifest->set_catalog_size(root_catalog_info.size);
1182  manifest->set_root_path("");
1183  manifest->set_ttl(root_catalog_info.ttl);
1184  manifest->set_revision(root_catalog_info.revision);
1185 
1186  return true;
1187 }
1188 
1189 
1217  const bool stop_for_tweaks) {
1218  // prepare environment for parallel processing
1219  Future<CatalogInfo> root_catalog_info_future;
1220  CatalogUploadContext upload_context;
1221  upload_context.root_catalog_info = &root_catalog_info_future;
1222  upload_context.stop_for_tweaks = stop_for_tweaks;
1223 
1224  spooler_->RegisterListener(
1225  &WritableCatalogManager::CatalogUploadCallback, this, upload_context);
1226 
1227  // find dirty leaf catalogs and annotate non-leaf catalogs (dirty child count)
1228  // post-condition: the entire catalog tree is ready for concurrent processing
1229  WritableCatalogList leafs_to_snapshot;
1230  GetModifiedCatalogLeafs(&leafs_to_snapshot);
1231 
1232  // finalize and schedule the catalog processing
1233  WritableCatalogList::const_iterator i = leafs_to_snapshot.begin();
1234  const WritableCatalogList::const_iterator iend = leafs_to_snapshot.end();
1235  for (; i != iend; ++i) {
1236  FinalizeCatalog(*i, stop_for_tweaks);
1238  }
1239 
1240  LogCvmfs(kLogCatalog, kLogVerboseMsg, "waiting for upload of catalogs");
1241  CatalogInfo& root_catalog_info = root_catalog_info_future.Get();
1242  spooler_->WaitForUpload();
1243 
1244  spooler_->UnregisterListeners();
1245  return root_catalog_info;
1246 }
1247 
1248 
1250  const bool stop_for_tweaks) {
1251  // update meta information of this catalog
1252  LogCvmfs(kLogCatalog, kLogVerboseMsg, "creating snapshot of catalog '%s'",
1253  catalog->mountpoint().c_str());
1254 
1255  catalog->UpdateCounters();
1256  catalog->UpdateLastModified();
1257  catalog->IncrementRevision();
1258 
1259  // update the previous catalog revision pointer
1260  if (catalog->IsRoot()) {
1261  LogCvmfs(kLogCatalog, kLogVerboseMsg, "setting '%s' as previous revision "
1262  "for root catalog",
1263  base_hash().ToStringWithSuffix().c_str());
1264  catalog->SetPreviousRevision(base_hash());
1265  } else {
1266  // Multiple catalogs might query the parent concurrently
1267  SyncLock();
1268  shash::Any hash_previous;
1269  uint64_t size_previous;
1270  const bool retval =
1271  catalog->parent()->FindNested(catalog->mountpoint(),
1272  &hash_previous, &size_previous);
1273  assert(retval);
1274  SyncUnlock();
1275 
1276  LogCvmfs(kLogCatalog, kLogVerboseMsg, "found '%s' as previous revision "
1277  "for nested catalog '%s'",
1278  hash_previous.ToStringWithSuffix().c_str(),
1279  catalog->mountpoint().c_str());
1280  catalog->SetPreviousRevision(hash_previous);
1281  }
1282  catalog->Commit();
1283 
1284  // check if catalog has too many entries
1285  uint64_t catalog_limit = uint64_t(1000) *
1286  uint64_t((catalog->IsRoot()
1289  if ((catalog_limit > 0) &&
1290  (catalog->GetCounters().GetSelfEntries() > catalog_limit)) {
1292  "%s: catalog at %s has more than %lu entries (%lu). "
1293  "Large catalogs stress the CernVM-FS transport infrastructure. "
1294  "Please split it into nested catalogs or increase the limit.",
1295  enforce_limits_ ? "FATAL" : "WARNING",
1296  (catalog->IsRoot() ? "/" : catalog->mountpoint().c_str()),
1297  catalog_limit, catalog->GetCounters().GetSelfEntries());
1298  if (enforce_limits_)
1299  PANIC(kLogStderr, "catalog at %s has more than %u entries (%u). ",
1300  (catalog->IsRoot() ? "/" : catalog->mountpoint().c_str()),
1301  catalog_limit, catalog->GetCounters().GetSelfEntries());
1302  }
1303 
1304  // allow for manual adjustments in the catalog
1305  if (stop_for_tweaks) {
1306  LogCvmfs(kLogCatalog, kLogStdout, "Allowing for tweaks in %s at %s "
1307  "(hit return to continue)",
1308  catalog->database_path().c_str(), catalog->mountpoint().c_str());
1309  int read_char = getchar();
1310  assert(read_char != EOF);
1311  }
1312 
1313  // compaction of bloated catalogs (usually after high database churn)
1314  catalog->VacuumDatabaseIfNecessary();
1315 }
1316 
1317 
1319  WritableCatalog *catalog) {
1320  {
1322  // register catalog object for WritableCatalogManager::CatalogUploadCallback
1323  catalog_processing_map_[catalog->database_path()] = catalog;
1324  }
1325  spooler_->ProcessCatalog(catalog->database_path());
1326 }
1327 
1337  const upload::SpoolerResult &result) {
1338  std::string tmp_catalog_path;
1339  const std::string cache_catalog_path = dir_cache_ + "/"
1341  FILE *fcatalog = CreateTempFile(dir_cache_ + "/txn/catalog", 0666,
1342  "w", &tmp_catalog_path);
1343  if (!fcatalog) {
1345  "Creating file for temporary catalog failed: %s",
1346  tmp_catalog_path.c_str());
1347  }
1348  CopyPath2File(result.local_path.c_str(), fcatalog);
1349  (void) fclose(fcatalog);
1350 
1351  if (rename(tmp_catalog_path.c_str(), cache_catalog_path.c_str()) != 0) {
1353  "Failed to copy catalog from %s to cache %s",
1354  result.local_path.c_str(), cache_catalog_path.c_str());
1355  }
1356  return true;
1357 }
1358 
1360  const upload::SpoolerResult &result,
1361  const CatalogUploadContext catalog_upload_context) {
1362  if (result.return_code != 0) {
1363  PANIC(kLogStderr, "failed to upload '%s' (retval: %d)",
1364  result.local_path.c_str(), result.return_code);
1365  }
1366 
1367  // retrieve the catalog object based on the callback information
1368  // see WritableCatalogManager::ScheduleCatalogProcessing()
1369  WritableCatalog *catalog = NULL;
1370  {
1372  std::map<std::string, WritableCatalog*>::iterator c =
1373  catalog_processing_map_.find(result.local_path);
1374  assert(c != catalog_processing_map_.end());
1375  catalog = c->second;
1376  }
1377 
1378  uint64_t catalog_size = GetFileSize(result.local_path);
1379  assert(catalog_size > 0);
1380 
1381  if (UseLocalCache()) {
1382  CopyCatalogToLocalCache(result);
1383  }
1384 
1385  SyncLock();
1386  if (catalog->HasParent()) {
1387  // finalized nested catalogs will update their parent's pointer and schedule
1388  // them for processing (continuation) if the 'dirty children count' == 0
1389  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating nested catalog link");
1390  WritableCatalog *parent = catalog->GetWritableParent();
1391 
1392  parent->UpdateNestedCatalog(catalog->mountpoint().ToString(),
1393  result.content_hash,
1394  catalog_size,
1395  catalog->delta_counters_);
1396  catalog->delta_counters_.SetZero();
1397 
1398  const int remaining_dirty_children =
1400 
1401  SyncUnlock();
1402 
1403  // continuation of the dirty catalog tree traversal
1404  // see WritableCatalogManager::SnapshotCatalogs()
1405  if (remaining_dirty_children == 0) {
1406  FinalizeCatalog(parent, catalog_upload_context.stop_for_tweaks);
1407  ScheduleCatalogProcessing(parent);
1408  }
1409 
1410  } else if (catalog->IsRoot()) {
1411  // once the root catalog is reached, we are done with processing and report
1412  // back to the main via a Future<> and provide the necessary information
1413  CatalogInfo root_catalog_info;
1414  root_catalog_info.size = catalog_size;
1415  root_catalog_info.ttl = catalog->GetTTL();
1416  root_catalog_info.content_hash = result.content_hash;
1417  root_catalog_info.revision = catalog->GetRevision();
1418  catalog_upload_context.root_catalog_info->Set(root_catalog_info);
1419  SyncUnlock();
1420  } else {
1421  PANIC(kLogStderr, "inconsistent state detected");
1422  }
1423 }
1424 
1425 
1437  Catalog *catalog,
1438  WritableCatalogList *result) const {
1439  WritableCatalog *wr_catalog = static_cast<WritableCatalog *>(catalog);
1440 
1441  // Look for dirty catalogs in the descendants of *catalog
1442  int dirty_children = 0;
1443  CatalogList children = wr_catalog->GetChildren();
1444  CatalogList::const_iterator i = children.begin();
1445  const CatalogList::const_iterator iend = children.end();
1446  for (; i != iend; ++i) {
1447  if (GetModifiedCatalogLeafsRecursively(*i, result)) {
1448  ++dirty_children;
1449  }
1450  }
1451 
1452  // a catalog is dirty if itself or one of its children has changed
1453  // a leaf catalog doesn't have any dirty children
1454  wr_catalog->set_dirty_children(dirty_children);
1455  const bool is_dirty = wr_catalog->IsDirty() || dirty_children > 0;
1456  const bool is_leaf = dirty_children == 0;
1457  if (is_dirty && is_leaf) {
1458  result->push_back(const_cast<WritableCatalog *>(wr_catalog));
1459  }
1460 
1461  return is_dirty;
1462 }
1463 
1464 
1466  CatalogList catalog_list = GetCatalogs();
1467  reverse(catalog_list.begin(), catalog_list.end());
1468  for (unsigned i = 0; i < catalog_list.size(); ++i) {
1469  FixWeight(static_cast<WritableCatalog*>(catalog_list[i]));
1470  }
1471 }
1472 
1474  // firstly check underflow because they can provoke overflows
1475  if (catalog->GetNumEntries() < min_weight_ &&
1476  !catalog->IsRoot() &&
1477  catalog->IsAutogenerated()) {
1479  "Deleting an autogenerated catalog in '%s'",
1480  catalog->mountpoint().c_str());
1481  // Remove the .cvmfscatalog and .cvmfsautocatalog files first
1482  string path = catalog->mountpoint().ToString();
1483  catalog->RemoveEntry(path + "/.cvmfscatalog");
1484  catalog->RemoveEntry(path + "/.cvmfsautocatalog");
1485  // Remove the actual catalog
1486  string catalog_path = catalog->mountpoint().ToString().substr(1);
1487  RemoveNestedCatalog(catalog_path);
1488  } else if (catalog->GetNumEntries() > max_weight_) {
1489  CatalogBalancer<WritableCatalogManager> catalog_balancer(this);
1490  catalog_balancer.Balance(catalog);
1491  }
1492 }
1493 
1494 
1495 //****************************************************************************
1496 // Workaround -- Serialized Catalog Committing
1497 
1499  const Catalog *catalog,
1500  WritableCatalogList *result) const
1501 {
1502  // A catalog must be snapshot, if itself or one of it's descendants is dirty.
1503  // So we traverse the catalog tree recursively and look for dirty catalogs
1504  // on the way.
1505  const WritableCatalog *wr_catalog =
1506  static_cast<const WritableCatalog *>(catalog);
1507  // This variable will contain the number of dirty catalogs in the sub tree
1508  // with *catalog as it's root.
1509  int dirty_catalogs = (wr_catalog->IsDirty()) ? 1 : 0;
1510 
1511  // Look for dirty catalogs in the descendants of *catalog
1512  CatalogList children = wr_catalog->GetChildren();
1513  for (CatalogList::const_iterator i = children.begin(), iEnd = children.end();
1514  i != iEnd; ++i)
1515  {
1516  dirty_catalogs += GetModifiedCatalogsRecursively(*i, result);
1517  }
1518 
1519  // If we found a dirty catalog in the checked sub tree, the root (*catalog)
1520  // must be snapshot and ends up in the result list
1521  if (dirty_catalogs > 0)
1522  result->push_back(const_cast<WritableCatalog *>(wr_catalog));
1523 
1524  // tell the upper layer about number of catalogs
1525  return dirty_catalogs;
1526 }
1527 
1528 
1530  const upload::SpoolerResult &result,
1531  const CatalogUploadContext unused)
1532 {
1533  if (result.return_code != 0) {
1534  PANIC(kLogStderr, "failed to upload '%s' (retval: %d)",
1535  result.local_path.c_str(), result.return_code);
1536  }
1537 
1538  if (UseLocalCache()) {
1539  CopyCatalogToLocalCache(result);
1540  }
1541 
1542  unlink(result.local_path.c_str());
1543 }
1544 
1545 
1548  const bool stop_for_tweaks)
1549 {
1550  LogCvmfs(kLogCvmfs, kLogStdout, "Serialized committing of file catalogs...");
1551  reinterpret_cast<WritableCatalog *>(GetRootCatalog())->SetDirty();
1552  WritableCatalogList catalogs_to_snapshot;
1553  GetModifiedCatalogs(&catalogs_to_snapshot);
1554  CatalogUploadContext unused;
1555  unused.root_catalog_info = NULL;
1556  unused.stop_for_tweaks = false;
1557  spooler_->RegisterListener(
1559 
1560  CatalogInfo root_catalog_info;
1561  WritableCatalogList::const_iterator i = catalogs_to_snapshot.begin();
1562  const WritableCatalogList::const_iterator iend = catalogs_to_snapshot.end();
1563  for (; i != iend; ++i) {
1564  FinalizeCatalog(*i, stop_for_tweaks);
1565 
1566  // Compress and upload catalog
1567  shash::Any hash_catalog(spooler_->GetHashAlgorithm(),
1569  if (!zlib::CompressPath2Null((*i)->database_path(),
1570  &hash_catalog))
1571  {
1572  PANIC(kLogStderr, "could not compress catalog %s",
1573  (*i)->mountpoint().ToString().c_str());
1574  }
1575 
1576  int64_t catalog_size = GetFileSize((*i)->database_path());
1577  assert(catalog_size > 0);
1578 
1579  if ((*i)->HasParent()) {
1580  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating nested catalog link");
1581  WritableCatalog *parent = (*i)->GetWritableParent();
1582  parent->UpdateNestedCatalog((*i)->mountpoint().ToString(), hash_catalog,
1583  catalog_size, (*i)->delta_counters_);
1584  (*i)->delta_counters_.SetZero();
1585  } else if ((*i)->IsRoot()) {
1586  root_catalog_info.size = catalog_size;
1587  root_catalog_info.ttl = (*i)->GetTTL();
1588  root_catalog_info.content_hash = hash_catalog;
1589  root_catalog_info.revision = (*i)->GetRevision();
1590  } else {
1591  PANIC(kLogStderr, "inconsistent state detected");
1592  }
1593 
1594  spooler_->ProcessCatalog((*i)->database_path());
1595  }
1596  spooler_->WaitForUpload();
1597 
1598  spooler_->UnregisterListeners();
1599  return root_catalog_info;
1600 }
1601 
1602 } // namespace catalog
bool CompressPath2Null(const string &src, shash::Any *compressed_hash)
Definition: compression.cc:515
uint32_t linkcount() const
int return_code
the return value of the spooler operation
bool IsExternalFile() const
const Counters & GetCounters() const
Definition: catalog.h:175
void AddChunkedFile(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory, const FileChunkList &file_chunks)
bool IsNull() const
Definition: hash.h:383
const manifest::Manifest * manifest() const
Definition: repository.h:125
void set_base_hash(const shash::Any &hash)
bool IsRoot() const
Definition: catalog.h:193
ShortString< kDefaultMaxName, 1 > NameString
Definition: shortstring.h:218
void CloneTree(const std::string &from_dir, const std::string &to_dir)
void AddHardlinkGroup(const DirectoryEntryBaseList &entries, const XattrList &xattrs, const std::string &parent_directory, const FileChunkList &file_chunks)
const std::vector< Catalog * > & GetCatalogs() const
Definition: catalog_mgr.h:343
void set_dirty_children(const int count)
Definition: catalog_rw.h:143
void set_is_chunked_file(const bool val)
void AddDirectory(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory)
std::string database_path() const
Definition: catalog.h:184
bool IsDirectory() const
void set_catalog_hash(const shash::Any &catalog_hash)
Definition: manifest.h:110
NameString GetFileName(const PathString &path)
Definition: shortstring.cc:29
Catalog * LoadFreeCatalog(const PathString &mountpoint, const shash::Any &hash)
bool IsChunkedFile() const
inode_t inode_
void Clear()
Definition: xattr.h:41
bool HasParent() const
Definition: catalog.h:200
bool MountSubtree(const PathString &path, const Catalog *entry_point, bool can_listing, Catalog **leaf_catalog)
void RemoveDirectory(const std::string &directory_path)
uint32_t GetMaxLinkId() const
Definition: catalog_rw.cc:124
void UpdateNestedCatalog(const std::string &path, const shash::Any &hash, const uint64_t size, const DeltaCounters &child_counters)
Definition: catalog_rw.cc:620
#define PANIC(...)
Definition: exception.h:29
Definition: future.h:32
FILE * CreateTempFile(const std::string &path_prefix, const int mode, const char *open_flags, std::string *final_path)
Definition: posix.cc:1016
uint64_t size() const
WritableCatalog * GetHostingCatalog(const std::string &path)
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:61
void set_linkcount(const uint32_t linkcount)
void set_is_nested_catalog_root(const bool val)
bool IsDirty() const
Definition: catalog_rw.h:57
void FixWeight(WritableCatalog *catalog)
std::string ToStringWithSuffix() const
Definition: hash.h:304
gid_t gid_
void Balance(catalog_t *catalog)
bool LookupPath(const PathString &path, DirectoryEntry *dirent) const
Definition: catalog.h:124
void ScheduleCatalogProcessing(WritableCatalog *catalog)
std::map< std::string, WritableCatalog * > catalog_processing_map_
std::string CreateTempPath(const std::string &path_prefix, const int mode)
Definition: posix.cc:1045
void SetTTL(const uint64_t new_ttl)
static DeltaCounters Diff(const Counters &from, const Counters &to)
int GetModifiedCatalogsRecursively(const Catalog *catalog, WritableCatalogList *result) const
Counters_t GetSelfEntries() const
void set_revision(const uint64_t revision)
Definition: manifest.h:94
void InsertNestedCatalog(const std::string &mountpoint, Catalog *attached_reference, const shash::Any content_hash, const uint64_t size)
Definition: catalog_rw.cc:502
void TouchEntry(const DirectoryEntryBase &entry, const XattrList &xattrs, const shash::Md5 &path_hash)
Definition: catalog_rw.cc:224
uint64_t GetTTL() const
Definition: catalog.cc:495
assert((mem||(size==0))&&"Out Of Memory")
bool FindCatalog(const std::string &path, WritableCatalog **result, DirectoryEntry *dirent=NULL)
bool Commit(const bool stop_for_tweaks, const uint64_t manual_revision, manifest::Manifest *manifest)
CatalogList GetChildren() const
Definition: catalog.cc:752
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
void SetPreviousRevision(const shash::Any &hash)
Definition: catalog_rw.cc:352
Catalog * parent() const
Definition: catalog.h:180
pthread_mutex_t * catalog_processing_lock_
Catalog * FindChild(const PathString &mountpoint) const
Definition: catalog.cc:808
std::vector< WritableCatalog * > WritableCatalogList
Definition: catalog_rw.h:203
shash::Any checksum() const
void CloneTreeImpl(const PathString &source_dir, const std::string &dest_parent_dir, const NameString &dest_name)
const unsigned kLookupDefault
Definition: catalog_mgr.h:43
uint64_t size_
bool CopyPath2File(const std::string &src, FILE *fdest)
Definition: compression.cc:46
T & Get()
Definition: future.h:66
void AddAsSubtree(DeltaCounters *delta) const
bool IsNestedCatalogMountpoint() const
bool IsTransitionPoint(const std::string &mountpoint)
Algorithms
Definition: hash.h:41
bool Listing(const PathString &path, DirectoryEntryList *listing, const bool expand_symlink)
void CatalogUploadCallback(const upload::SpoolerResult &result, const CatalogUploadContext clg_upload_context)
bool IsNestedCatalogRoot() const
NameString name_
void TouchDirectory(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &directory_path)
uint64_t GetNumEntries() const
Definition: catalog.cc:546
uint64_t GetRevision() const
Definition: catalog.cc:528
void AddFileChunk(const std::string &entry_path, const FileChunk &chunk)
Definition: catalog_rw.cc:264
bool IsAutogenerated() const
Definition: catalog.h:194
std::vector< DirectoryEntry > DirectoryEntryList
NameString name() const
static manifest::Manifest * CreateRepository(const std::string &dir_temp, const bool volatile_content, const std::string &voms_authz, upload::Spooler *spooler)
bool AttachCatalog(const std::string &db_path, Catalog *new_catalog)
void SplitPath(const std::string &path, std::string *dirname, std::string *filename)
Definition: posix.cc:113
bool HasXattrs() const
void ActivateCatalog(Catalog *catalog)
std::string local_path
the local_path previously given as input
bool LookupXattrsPath(const PathString &path, XattrList *xattrs) const
Definition: catalog.h:128
void ShrinkHardlinkGroup(const std::string &remove_path)
bool IsRegular() const
bool CopyCatalogToLocalCache(const upload::SpoolerResult &result)
bool GetModifiedCatalogLeafsRecursively(Catalog *catalog, WritableCatalogList *result) const
const char kSuffixCatalog
Definition: hash.h:54
uint32_t linkcount_
const std::string & dir_temp() const
void PopulateToParent(DeltaCounters *parent) const
bool InsertInitialValues(const std::string &root_path, const bool volatile_content, const std::string &voms_authz, const DirectoryEntry &root_entry=DirectoryEntry(kDirentNegative))
Definition: catalog_sql.cc:283
CatalogInfo SnapshotCatalogsSerialized(const bool stop_for_tweaks)
CatalogInfo SnapshotCatalogs(const bool stop_for_tweaks)
PathString mountpoint() const
Definition: catalog.h:179
time_t mtime_
static const inode_t kInvalidInode
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:80
void TakeDatabaseFileOwnership()
Definition: catalog.cc:479
bool SetVOMSAuthz(const std::string &voms_authz)
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:285
void Clone(const std::string from, const std::string to)
std::vector< Catalog * > CatalogList
Definition: catalog_mgr.h:237
void RemoveFile(const std::string &file_path)
bool IsBindMountpoint() const
std::string MakeRelativePath(const std::string &relative_path) const
void CatalogUploadSerializedCallback(const upload::SpoolerResult &result, const CatalogUploadContext unused)
void RemoveNestedCatalog(const std::string &mountpoint, Catalog **attached_reference)
Definition: catalog_rw.cc:561
bool IsEmpty() const
Definition: bigvector.h:72
void AddEntry(const DirectoryEntry &entry, const XattrList &xattr, const std::string &entry_path, const std::string &parent_path)
void RemoveNestedCatalog(const std::string &mountpoint, const bool merge=true)
void set_ttl(const uint32_t ttl)
Definition: manifest.h:93
shash::Algorithms GetHashAlgorithm() const
Definition: manifest.h:91
std::string MakePathWithoutSuffix() const
Definition: hash.h:335
std::string ToString() const
Definition: shortstring.h:141
std::vector< NestedCatalog > NestedCatalogList
Definition: catalog.h:208
bool IsEmpty() const
Definition: shortstring.h:137
unsigned int mode_
bool ListFileChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks)
void Partition(WritableCatalog *new_nested_catalog)
Definition: catalog_rw.cc:360
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:217
void SetRevision(const uint64_t new_revision)
Definition: catalog_rw.cc:329
Definition: mutex.h:42
void set_hardlink_group(const uint32_t group)
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
WritableCatalog * GetWritableParent() const
Definition: catalog_rw.h:136
void set_catalog_size(const uint64_t catalog_size)
Definition: manifest.h:107
DeltaCounters delta_counters_
Definition: catalog_rw.h:163
bool FindNested(const PathString &mountpoint, shash::Any *hash, uint64_t *size) const
Definition: catalog.cc:690
std::string GetFullPath(const std::string &parent_directory) const
bool LookupXattrs(const PathString &path, XattrList *xattrs)
Catalog * MountCatalog(const PathString &mountpoint, const shash::Any &hash, Catalog *parent_catalog)
int64_t GetFileSize(const std::string &path)
Definition: posix.cc:812
std::vector< DirectoryEntryBase > DirectoryEntryBaseList
const int kLogVerboseMsg
void set_has_alt_catalog_path(const bool &has_alt_path)
Definition: manifest.h:116
unsigned GetLength() const
Definition: shortstring.h:131
shash::Any checksum_
CatalogT * FindCatalog(const PathString &path) const
std::string MakePath() const
Definition: hash.h:316
void AddFile(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory)
const char * c_str() const
Definition: shortstring.h:145
void SwapNestedCatalog(const string &mountpoint, const shash::Any &new_hash, const uint64_t new_size)
void RemoveEntry(const std::string &entry_path)
Definition: catalog_rw.cc:184
const char * GetChars() const
Definition: shortstring.h:123
virtual bool IsWritable() const
Definition: catalog.h:201
void GetModifiedCatalogLeafs(WritableCatalogList *result) const
Catalog * CreateCatalog(const PathString &mountpoint, const shash::Any &catalog_hash, Catalog *parent_catalog)
void FinalizeCatalog(WritableCatalog *catalog, const bool stop_for_tweaks)
void IncLinkcount(const std::string &path_within_group, const int delta)
Definition: catalog_rw.cc:208
void GraftNestedCatalog(const string &mountpoint, const shash::Any &new_hash, const uint64_t new_size)
const Item * AtPtr(const size_t index) const
Definition: bigvector.h:55
void set_is_nested_catalog_mountpoint(const bool val)
bool CompressPath2Path(const string &src, const string &dest)
Definition: compression.cc:318
void CreateNestedCatalog(const std::string &mountpoint)
void UpdateEntry(const DirectoryEntry &entry, const shash::Md5 &path_hash)
Definition: catalog_rw.cc:252
static DerivedT * Create(const std::string &filename)
Definition: sql_impl.h:30
void GetModifiedCatalogs(WritableCatalogList *result) const
size_t size() const
Definition: bigvector.h:121
uid_t uid_
void set_root_path(const std::string &root_path)
Definition: manifest.h:122
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528
const shash::Any & base_hash() const