CernVM-FS  2.12.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 
1007 
1011 bool WritableCatalogManager::IsTransitionPoint(const string &mountpoint) {
1012  const string path = MakeRelativePath(mountpoint);
1013 
1014  SyncLock();
1015  WritableCatalog *catalog;
1016  DirectoryEntry entry;
1017  if (!FindCatalog(path, &catalog, &entry)) {
1018  PANIC(kLogStderr, "catalog for directory '%s' cannot be found",
1019  path.c_str());
1020  }
1021  const bool result = entry.IsNestedCatalogRoot();
1022  SyncUnlock();
1023  return result;
1024 }
1025 
1026 
1028  // TODO(jblomer): meant for micro catalogs
1029 }
1030 
1031 
1032 void WritableCatalogManager::SetTTL(const uint64_t new_ttl) {
1033  SyncLock();
1034  reinterpret_cast<WritableCatalog *>(GetRootCatalog())->SetTTL(new_ttl);
1035  SyncUnlock();
1036 }
1037 
1038 
1039 bool WritableCatalogManager::SetVOMSAuthz(const std::string &voms_authz) {
1040  bool result;
1041  SyncLock();
1042  result = reinterpret_cast<WritableCatalog *>(
1043  GetRootCatalog())->SetVOMSAuthz(voms_authz);
1044  SyncUnlock();
1045  return result;
1046 }
1047 
1048 
1049 bool WritableCatalogManager::Commit(const bool stop_for_tweaks,
1050  const uint64_t manual_revision,
1052  WritableCatalog *root_catalog =
1053  reinterpret_cast<WritableCatalog *>(GetRootCatalog());
1054  root_catalog->SetDirty();
1055 
1056  // set root catalog revision to manually provided number if available
1057  if (manual_revision > 0) {
1058  const uint64_t revision = root_catalog->GetRevision();
1059  if (revision >= manual_revision) {
1061  "Manual revision (%" PRIu64 ") must not be "
1062  "smaller than the current root catalog's (%" PRIu64
1063  "). Skipped!", manual_revision, revision);
1064  } else {
1065  // Gets incremented by FinalizeCatalog() afterwards!
1066  root_catalog->SetRevision(manual_revision - 1);
1067  }
1068  }
1069 
1070  // do the actual catalog snapshotting and upload
1071  CatalogInfo root_catalog_info;
1072  if (getenv("_CVMFS_SERIALIZED_CATALOG_PROCESSING_") == NULL)
1073  root_catalog_info = SnapshotCatalogs(stop_for_tweaks);
1074  else
1075  root_catalog_info = SnapshotCatalogsSerialized(stop_for_tweaks);
1076  if (spooler_->GetNumberOfErrors() > 0) {
1077  LogCvmfs(kLogCatalog, kLogStderr, "failed to commit catalogs");
1078  return false;
1079  }
1080 
1081  // .cvmfspublished export
1082  LogCvmfs(kLogCatalog, kLogVerboseMsg, "Committing repository manifest");
1083  set_base_hash(root_catalog_info.content_hash);
1084 
1085  manifest->set_catalog_hash(root_catalog_info.content_hash);
1086  manifest->set_catalog_size(root_catalog_info.size);
1087  manifest->set_root_path("");
1088  manifest->set_ttl(root_catalog_info.ttl);
1089  manifest->set_revision(root_catalog_info.revision);
1090 
1091  return true;
1092 }
1093 
1094 
1122  const bool stop_for_tweaks) {
1123  // prepare environment for parallel processing
1124  Future<CatalogInfo> root_catalog_info_future;
1125  CatalogUploadContext upload_context;
1126  upload_context.root_catalog_info = &root_catalog_info_future;
1127  upload_context.stop_for_tweaks = stop_for_tweaks;
1128 
1129  spooler_->RegisterListener(
1130  &WritableCatalogManager::CatalogUploadCallback, this, upload_context);
1131 
1132  // find dirty leaf catalogs and annotate non-leaf catalogs (dirty child count)
1133  // post-condition: the entire catalog tree is ready for concurrent processing
1134  WritableCatalogList leafs_to_snapshot;
1135  GetModifiedCatalogLeafs(&leafs_to_snapshot);
1136 
1137  // finalize and schedule the catalog processing
1138  WritableCatalogList::const_iterator i = leafs_to_snapshot.begin();
1139  const WritableCatalogList::const_iterator iend = leafs_to_snapshot.end();
1140  for (; i != iend; ++i) {
1141  FinalizeCatalog(*i, stop_for_tweaks);
1143  }
1144 
1145  LogCvmfs(kLogCatalog, kLogVerboseMsg, "waiting for upload of catalogs");
1146  CatalogInfo& root_catalog_info = root_catalog_info_future.Get();
1147  spooler_->WaitForUpload();
1148 
1149  spooler_->UnregisterListeners();
1150  return root_catalog_info;
1151 }
1152 
1153 
1155  const bool stop_for_tweaks) {
1156  // update meta information of this catalog
1157  LogCvmfs(kLogCatalog, kLogVerboseMsg, "creating snapshot of catalog '%s'",
1158  catalog->mountpoint().c_str());
1159 
1160  catalog->UpdateCounters();
1161  catalog->UpdateLastModified();
1162  catalog->IncrementRevision();
1163 
1164  // update the previous catalog revision pointer
1165  if (catalog->IsRoot()) {
1166  LogCvmfs(kLogCatalog, kLogVerboseMsg, "setting '%s' as previous revision "
1167  "for root catalog",
1168  base_hash().ToStringWithSuffix().c_str());
1169  catalog->SetPreviousRevision(base_hash());
1170  } else {
1171  // Multiple catalogs might query the parent concurrently
1172  SyncLock();
1173  shash::Any hash_previous;
1174  uint64_t size_previous;
1175  const bool retval =
1176  catalog->parent()->FindNested(catalog->mountpoint(),
1177  &hash_previous, &size_previous);
1178  assert(retval);
1179  SyncUnlock();
1180 
1181  LogCvmfs(kLogCatalog, kLogVerboseMsg, "found '%s' as previous revision "
1182  "for nested catalog '%s'",
1183  hash_previous.ToStringWithSuffix().c_str(),
1184  catalog->mountpoint().c_str());
1185  catalog->SetPreviousRevision(hash_previous);
1186  }
1187  catalog->Commit();
1188 
1189  // check if catalog has too many entries
1190  uint64_t catalog_limit = uint64_t(1000) *
1191  uint64_t((catalog->IsRoot()
1194  if ((catalog_limit > 0) &&
1195  (catalog->GetCounters().GetSelfEntries() > catalog_limit)) {
1197  "%s: catalog at %s has more than %lu entries (%lu). "
1198  "Large catalogs stress the CernVM-FS transport infrastructure. "
1199  "Please split it into nested catalogs or increase the limit.",
1200  enforce_limits_ ? "FATAL" : "WARNING",
1201  (catalog->IsRoot() ? "/" : catalog->mountpoint().c_str()),
1202  catalog_limit, catalog->GetCounters().GetSelfEntries());
1203  if (enforce_limits_)
1204  PANIC(kLogStderr, "catalog at %s has more than %u entries (%u). ",
1205  (catalog->IsRoot() ? "/" : catalog->mountpoint().c_str()),
1206  catalog_limit, catalog->GetCounters().GetSelfEntries());
1207  }
1208 
1209  // allow for manual adjustments in the catalog
1210  if (stop_for_tweaks) {
1211  LogCvmfs(kLogCatalog, kLogStdout, "Allowing for tweaks in %s at %s "
1212  "(hit return to continue)",
1213  catalog->database_path().c_str(), catalog->mountpoint().c_str());
1214  int read_char = getchar();
1215  assert(read_char != EOF);
1216  }
1217 
1218  // compaction of bloated catalogs (usually after high database churn)
1219  catalog->VacuumDatabaseIfNecessary();
1220 }
1221 
1222 
1224  WritableCatalog *catalog) {
1225  {
1227  // register catalog object for WritableCatalogManager::CatalogUploadCallback
1228  catalog_processing_map_[catalog->database_path()] = catalog;
1229  }
1230  spooler_->ProcessCatalog(catalog->database_path());
1231 }
1232 
1242  const upload::SpoolerResult &result) {
1243  std::string tmp_catalog_path;
1244  const std::string cache_catalog_path = dir_cache_ + "/"
1246  FILE *fcatalog = CreateTempFile(dir_cache_ + "/txn/catalog", 0666,
1247  "w", &tmp_catalog_path);
1248  if (!fcatalog) {
1250  "Creating file for temporary catalog failed: %s",
1251  tmp_catalog_path.c_str());
1252  }
1253  CopyPath2File(result.local_path.c_str(), fcatalog);
1254  (void) fclose(fcatalog);
1255 
1256  if (rename(tmp_catalog_path.c_str(), cache_catalog_path.c_str()) != 0) {
1258  "Failed to copy catalog from %s to cache %s",
1259  result.local_path.c_str(), cache_catalog_path.c_str());
1260  }
1261  return true;
1262 }
1263 
1265  const upload::SpoolerResult &result,
1266  const CatalogUploadContext catalog_upload_context) {
1267  if (result.return_code != 0) {
1268  PANIC(kLogStderr, "failed to upload '%s' (retval: %d)",
1269  result.local_path.c_str(), result.return_code);
1270  }
1271 
1272  // retrieve the catalog object based on the callback information
1273  // see WritableCatalogManager::ScheduleCatalogProcessing()
1274  WritableCatalog *catalog = NULL;
1275  {
1277  std::map<std::string, WritableCatalog*>::iterator c =
1278  catalog_processing_map_.find(result.local_path);
1279  assert(c != catalog_processing_map_.end());
1280  catalog = c->second;
1281  }
1282 
1283  uint64_t catalog_size = GetFileSize(result.local_path);
1284  assert(catalog_size > 0);
1285 
1286  if (UseLocalCache()) {
1287  CopyCatalogToLocalCache(result);
1288  }
1289 
1290  SyncLock();
1291  if (catalog->HasParent()) {
1292  // finalized nested catalogs will update their parent's pointer and schedule
1293  // them for processing (continuation) if the 'dirty children count' == 0
1294  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating nested catalog link");
1295  WritableCatalog *parent = catalog->GetWritableParent();
1296 
1297  parent->UpdateNestedCatalog(catalog->mountpoint().ToString(),
1298  result.content_hash,
1299  catalog_size,
1300  catalog->delta_counters_);
1301  catalog->delta_counters_.SetZero();
1302 
1303  const int remaining_dirty_children =
1305 
1306  SyncUnlock();
1307 
1308  // continuation of the dirty catalog tree traversal
1309  // see WritableCatalogManager::SnapshotCatalogs()
1310  if (remaining_dirty_children == 0) {
1311  FinalizeCatalog(parent, catalog_upload_context.stop_for_tweaks);
1312  ScheduleCatalogProcessing(parent);
1313  }
1314 
1315  } else if (catalog->IsRoot()) {
1316  // once the root catalog is reached, we are done with processing and report
1317  // back to the main via a Future<> and provide the necessary information
1318  CatalogInfo root_catalog_info;
1319  root_catalog_info.size = catalog_size;
1320  root_catalog_info.ttl = catalog->GetTTL();
1321  root_catalog_info.content_hash = result.content_hash;
1322  root_catalog_info.revision = catalog->GetRevision();
1323  catalog_upload_context.root_catalog_info->Set(root_catalog_info);
1324  SyncUnlock();
1325  } else {
1326  PANIC(kLogStderr, "inconsistent state detected");
1327  }
1328 }
1329 
1330 
1342  Catalog *catalog,
1343  WritableCatalogList *result) const {
1344  WritableCatalog *wr_catalog = static_cast<WritableCatalog *>(catalog);
1345 
1346  // Look for dirty catalogs in the descendants of *catalog
1347  int dirty_children = 0;
1348  CatalogList children = wr_catalog->GetChildren();
1349  CatalogList::const_iterator i = children.begin();
1350  const CatalogList::const_iterator iend = children.end();
1351  for (; i != iend; ++i) {
1352  if (GetModifiedCatalogLeafsRecursively(*i, result)) {
1353  ++dirty_children;
1354  }
1355  }
1356 
1357  // a catalog is dirty if itself or one of its children has changed
1358  // a leaf catalog doesn't have any dirty children
1359  wr_catalog->set_dirty_children(dirty_children);
1360  const bool is_dirty = wr_catalog->IsDirty() || dirty_children > 0;
1361  const bool is_leaf = dirty_children == 0;
1362  if (is_dirty && is_leaf) {
1363  result->push_back(const_cast<WritableCatalog *>(wr_catalog));
1364  }
1365 
1366  return is_dirty;
1367 }
1368 
1369 
1371  CatalogList catalog_list = GetCatalogs();
1372  reverse(catalog_list.begin(), catalog_list.end());
1373  for (unsigned i = 0; i < catalog_list.size(); ++i) {
1374  FixWeight(static_cast<WritableCatalog*>(catalog_list[i]));
1375  }
1376 }
1377 
1379  // firstly check underflow because they can provoke overflows
1380  if (catalog->GetNumEntries() < min_weight_ &&
1381  !catalog->IsRoot() &&
1382  catalog->IsAutogenerated()) {
1384  "Deleting an autogenerated catalog in '%s'",
1385  catalog->mountpoint().c_str());
1386  // Remove the .cvmfscatalog and .cvmfsautocatalog files first
1387  string path = catalog->mountpoint().ToString();
1388  catalog->RemoveEntry(path + "/.cvmfscatalog");
1389  catalog->RemoveEntry(path + "/.cvmfsautocatalog");
1390  // Remove the actual catalog
1391  string catalog_path = catalog->mountpoint().ToString().substr(1);
1392  RemoveNestedCatalog(catalog_path);
1393  } else if (catalog->GetNumEntries() > max_weight_) {
1394  CatalogBalancer<WritableCatalogManager> catalog_balancer(this);
1395  catalog_balancer.Balance(catalog);
1396  }
1397 }
1398 
1399 
1400 //****************************************************************************
1401 // Workaround -- Serialized Catalog Committing
1402 
1404  const Catalog *catalog,
1405  WritableCatalogList *result) const
1406 {
1407  // A catalog must be snapshot, if itself or one of it's descendants is dirty.
1408  // So we traverse the catalog tree recursively and look for dirty catalogs
1409  // on the way.
1410  const WritableCatalog *wr_catalog =
1411  static_cast<const WritableCatalog *>(catalog);
1412  // This variable will contain the number of dirty catalogs in the sub tree
1413  // with *catalog as it's root.
1414  int dirty_catalogs = (wr_catalog->IsDirty()) ? 1 : 0;
1415 
1416  // Look for dirty catalogs in the descendants of *catalog
1417  CatalogList children = wr_catalog->GetChildren();
1418  for (CatalogList::const_iterator i = children.begin(), iEnd = children.end();
1419  i != iEnd; ++i)
1420  {
1421  dirty_catalogs += GetModifiedCatalogsRecursively(*i, result);
1422  }
1423 
1424  // If we found a dirty catalog in the checked sub tree, the root (*catalog)
1425  // must be snapshot and ends up in the result list
1426  if (dirty_catalogs > 0)
1427  result->push_back(const_cast<WritableCatalog *>(wr_catalog));
1428 
1429  // tell the upper layer about number of catalogs
1430  return dirty_catalogs;
1431 }
1432 
1433 
1435  const upload::SpoolerResult &result,
1436  const CatalogUploadContext unused)
1437 {
1438  if (result.return_code != 0) {
1439  PANIC(kLogStderr, "failed to upload '%s' (retval: %d)",
1440  result.local_path.c_str(), result.return_code);
1441  }
1442 
1443  if (UseLocalCache()) {
1444  CopyCatalogToLocalCache(result);
1445  }
1446 
1447  unlink(result.local_path.c_str());
1448 }
1449 
1450 
1453  const bool stop_for_tweaks)
1454 {
1455  LogCvmfs(kLogCvmfs, kLogStdout, "Serialized committing of file catalogs...");
1456  reinterpret_cast<WritableCatalog *>(GetRootCatalog())->SetDirty();
1457  WritableCatalogList catalogs_to_snapshot;
1458  GetModifiedCatalogs(&catalogs_to_snapshot);
1459  CatalogUploadContext unused;
1460  unused.root_catalog_info = NULL;
1461  unused.stop_for_tweaks = false;
1462  spooler_->RegisterListener(
1464 
1465  CatalogInfo root_catalog_info;
1466  WritableCatalogList::const_iterator i = catalogs_to_snapshot.begin();
1467  const WritableCatalogList::const_iterator iend = catalogs_to_snapshot.end();
1468  for (; i != iend; ++i) {
1469  FinalizeCatalog(*i, stop_for_tweaks);
1470 
1471  // Compress and upload catalog
1472  shash::Any hash_catalog(spooler_->GetHashAlgorithm(),
1474  if (!zlib::CompressPath2Null((*i)->database_path(),
1475  &hash_catalog))
1476  {
1477  PANIC(kLogStderr, "could not compress catalog %s",
1478  (*i)->mountpoint().ToString().c_str());
1479  }
1480 
1481  int64_t catalog_size = GetFileSize((*i)->database_path());
1482  assert(catalog_size > 0);
1483 
1484  if ((*i)->HasParent()) {
1485  LogCvmfs(kLogCatalog, kLogVerboseMsg, "updating nested catalog link");
1486  WritableCatalog *parent = (*i)->GetWritableParent();
1487  parent->UpdateNestedCatalog((*i)->mountpoint().ToString(), hash_catalog,
1488  catalog_size, (*i)->delta_counters_);
1489  (*i)->delta_counters_.SetZero();
1490  } else if ((*i)->IsRoot()) {
1491  root_catalog_info.size = catalog_size;
1492  root_catalog_info.ttl = (*i)->GetTTL();
1493  root_catalog_info.content_hash = hash_catalog;
1494  root_catalog_info.revision = (*i)->GetRevision();
1495  } else {
1496  PANIC(kLogStderr, "inconsistent state detected");
1497  }
1498 
1499  spooler_->ProcessCatalog((*i)->database_path());
1500  }
1501  spooler_->WaitForUpload();
1502 
1503  spooler_->UnregisterListeners();
1504  return root_catalog_info;
1505 }
1506 
1507 } // 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
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