5 #define __STDC_FORMAT_MACROS
31 WritableCatalogManager::WritableCatalogManager(
33 const std::string &stratum0,
34 const string &dir_temp,
38 const unsigned nested_kcatalog_limit,
39 const unsigned root_kcatalog_limit,
40 const unsigned file_mbyte_limit,
45 const std::string &dir_cache)
47 statistics, false, dir_cache, true )
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)
59 reinterpret_cast<pthread_mutex_t *
>(smalloc(
sizeof(pthread_mutex_t)));
60 int retval = pthread_mutex_init(
sync_lock_, NULL);
63 reinterpret_cast<pthread_mutex_t *
>(smalloc(
sizeof(pthread_mutex_t)));
109 const string &dir_temp,
110 const bool volatile_content,
111 const std::string &voms_authz,
115 string file_path = dir_temp +
"/new_root_catalog";
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();
130 string root_path =
"";
136 !new_clg_db->InsertInitialValues(root_path,
149 if (catalog_size < 0) {
150 unlink(file_path.c_str());
153 string file_path_compressed = file_path +
".compressed";
160 unlink(file_path.c_str());
163 unlink(file_path.c_str());
166 const string manifest_path = dir_temp +
"/manifest";
169 if (!voms_authz.empty()) {
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) {
179 file_path_compressed.c_str());
214 if (NULL == dirent) {
217 bool found = catalog->
LookupPath(ps_path, dirent);
227 const std::string &path)
231 if (!retval)
return NULL;
269 if (!
FindCatalog(parent_path, &catalog, &parent_entry)) {
271 directory_path.c_str());
280 parent_path.c_str());
285 parent_catalog->UpdateEntry(parent_entry, parent_path);
299 const std::string source) {
314 bool destination_already_present =
316 if (destination_already_present) {
321 std::string destination_dirname;
322 std::string destination_filename;
323 SplitPath(destination, &destination_dirname, &destination_filename);
326 NameString(destination_filename.c_str(), destination_filename.length()));
341 const std::string &to_dir)
344 if (from_dir.empty() || to_dir.empty())
350 if (relative_source == relative_dest) {
351 PANIC(
kLogStderr,
"cannot clone tree into itself ('%s')", to_dir.c_str());
353 if (
HasPrefix(relative_dest, relative_source +
"/",
false )) {
355 "cannot clone tree into sub directory of source '%s' --> '%s'",
356 from_dir.c_str(), to_dir.c_str());
361 PANIC(
kLogStderr,
"path '%s' cannot be found, aborting", from_dir.c_str());
370 PANIC(
kLogStderr,
"destination '%s' exists, aborting", to_dir.c_str());
373 const std::string dest_parent =
GetParentPath(relative_dest);
392 const std::string &dest_parent_dir,
396 dest_parent_dir.c_str(), dest_name.
ToString().c_str());
405 dest_dirent.name_.Assign(dest_name);
407 dest_dirent.set_is_nested_catalog_mountpoint(
false);
408 dest_dirent.set_is_nested_catalog_root(
false);
417 std::string dest_dir = dest_parent_dir;
418 if (!dest_dir.empty())
419 dest_dir.push_back(
'/');
428 retval =
Listing(relative_source, &ls,
false );
430 for (
unsigned i = 0; i < ls.size(); ++i) {
434 sub_path.
Append(ls[i].name().GetChars(), ls[i].name().GetLength());
436 if (ls[i].IsDirectory()) {
442 ls[i].set_hardlink_group(0);
443 ls[i].set_linkcount(1);
446 if (ls[i].HasXattrs()) {
451 if (ls[i].IsChunkedFile()) {
455 PathString(relative_sub_path), ls[i].hash_algorithm(), &chunks);
459 AddFile(ls[i], xattrs, dest_dir);
474 const std::string &parent_directory)
477 string directory_path = parent_path +
"/";
483 if (!
FindCatalog(parent_path, &catalog, &parent_entry)) {
485 directory_path.c_str());
490 catalog->
AddEntry(fixed_hardlink_count, xattrs,
491 directory_path, parent_path);
497 parent_path.c_str());
502 parent_catalog->UpdateEntry(parent_entry, parent_path);
517 const std::string &parent_directory)
520 const string file_path = entry.
GetFullPath(parent_path);
534 unsigned mbytes = entry.
size() / (1024 * 1024);
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.",
547 catalog->
AddEntry(entry, xattrs, file_path, parent_path);
555 const std::string &parent_directory,
561 full_entry.set_is_chunked_file(
true);
563 AddFile(full_entry, xattrs, parent_directory);
566 const string file_path = entry.
GetFullPath(parent_path);
575 for (
unsigned i = 0; i < file_chunks.
size(); ++i) {
592 const std::string &parent_directory,
595 assert(entries.size() >= 1);
597 if (entries.size() == 1) {
601 return AddFile(fix_linkcount, xattrs, parent_directory);
602 return AddChunkedFile(fix_linkcount, xattrs, parent_directory, file_chunks);
606 parent_directory.c_str(), entries[0].name().c_str());
613 unsigned mbytes = entries[0].size() / (1024 * 1024);
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.",
620 (parent_path + entries[0].name().ToString()).c_str(),
625 (parent_path + entries[0].name().ToString()).c_str(),
633 "catalog for hardlink group containing '%s' cannot be found",
634 parent_path.c_str());
645 for (DirectoryEntryBaseList::const_iterator i = entries.begin(),
646 iEnd = entries.end(); i != iEnd; ++i)
648 string file_path = parent_path +
"/";
649 file_path.append(i->name().GetChars(), i->name().GetLength());
658 catalog->
AddEntry(hardlink, xattrs, file_path, parent_path);
660 for (
unsigned i = 0; i < file_chunks.
size(); ++i) {
676 "catalog for hardlink group containing '%s' cannot be found",
677 remove_path.c_str());
695 const std::string &directory_path)
710 catalog->
TouchEntry(entry, xattrs, entry_path);
720 PathString transition_path(entry_path.data(), entry_path.length());
721 bool retval = catalog->
LookupPath(transition_path,
722 &potential_transition_point);
726 "updating transition point at %s", entry_path.c_str());
730 uint64_t nested_size;
731 retval = catalog->
FindNested(transition_path, &nested_hash, &nested_size);
734 nested_catalog =
MountCatalog(transition_path, nested_hash, catalog);
735 assert(nested_catalog != NULL);
739 TouchEntry(entry, xattrs, entry_path);
755 const PathString ps_nested_root_path(nested_root_path);
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());
774 const bool volatile_content =
false;
776 assert(NULL != new_catalog_db);
787 delete new_catalog_db;
788 new_catalog_db = NULL;
803 wr_new_catalog->TouchEntry(new_root_entry, xattrs, nested_root_path);
820 wr_new_catalog->ListOwnNestedCatalogs();
822 for (Catalog::NestedCatalogList::const_iterator i = grand_nested.begin(),
823 iEnd = grand_nested.end(); i != iEnd; ++i)
826 retval =
FindCatalog(i->mountpoint.ToString(), &grand_catalog);
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;
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());
879 "unable to delete the removed nested catalog database file '%s'",
902 const uint64_t new_size) {
914 "failed to swap nested catalog '%s': could not find parent '%s'",
915 nested_root_path.c_str(), parent_path.c_str());
921 if (old_attached_catalog) {
929 "failed to swap nested catalog '%s': already modified",
930 nested_root_path.c_str());
932 old_counters = old_attached_catalog->
GetCounters();
940 const bool old_found = parent->
FindNested(nested_root_ps, &old_hash,
945 "failed to swap nested catalog '%s': not found in parent",
946 nested_root_path.c_str());
950 if (!old_free_catalog.
IsValid()) {
953 "failed to swap nested catalog '%s': failed to load old catalog",
954 nested_root_path.c_str());
956 old_counters = old_free_catalog->GetCounters();
964 "failed to swap nested catalog '%s': failed to load new catalog",
965 nested_root_path.c_str());
971 const bool dirent_found = new_catalog->LookupPath(nested_root_ps, &dirent);
975 "failed to swap nested catalog '%s': missing dirent in new catalog",
976 nested_root_path.c_str());
979 const bool xattrs_found = new_catalog->LookupXattrsPath(nested_root_ps,
984 "failed to swap nested catalog '%s': missing xattrs in new catalog",
985 nested_root_path.c_str());
997 parent->
TouchEntry(dirent, xattrs, nested_root_path);
1001 new_catalog->GetCounters());
1050 const uint64_t manual_revision,
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);
1072 if (getenv(
"_CVMFS_SERIALIZED_CATALOG_PROCESSING_") == NULL)
1076 if (
spooler_->GetNumberOfErrors() > 0) {
1122 const bool stop_for_tweaks) {
1138 WritableCatalogList::const_iterator i = leafs_to_snapshot.begin();
1139 const WritableCatalogList::const_iterator iend = leafs_to_snapshot.end();
1140 for (; i != iend; ++i) {
1150 return root_catalog_info;
1155 const bool stop_for_tweaks) {
1168 base_hash().ToStringWithSuffix().c_str());
1174 uint64_t size_previous;
1177 &hash_previous, &size_previous);
1182 "for nested catalog '%s'",
1190 uint64_t catalog_limit = uint64_t(1000) *
1191 uint64_t((catalog->
IsRoot()
1194 if ((catalog_limit > 0) &&
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.",
1210 if (stop_for_tweaks) {
1212 "(hit return to continue)",
1214 int read_char = getchar();
1215 assert(read_char != EOF);
1243 std::string tmp_catalog_path;
1244 const std::string cache_catalog_path =
dir_cache_ +
"/"
1247 "w", &tmp_catalog_path);
1250 "Creating file for temporary catalog failed: %s",
1251 tmp_catalog_path.c_str());
1254 (void) fclose(fcatalog);
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());
1277 std::map<std::string, WritableCatalog*>::iterator c =
1280 catalog = c->second;
1284 assert(catalog_size > 0);
1303 const int remaining_dirty_children =
1310 if (remaining_dirty_children == 0) {
1315 }
else if (catalog->
IsRoot()) {
1319 root_catalog_info.
size = catalog_size;
1320 root_catalog_info.
ttl = catalog->
GetTTL();
1347 int dirty_children = 0;
1349 CatalogList::const_iterator i = children.begin();
1350 const CatalogList::const_iterator iend = children.end();
1351 for (; i != iend; ++i) {
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));
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]));
1384 "Deleting an autogenerated catalog in '%s'",
1389 catalog->
RemoveEntry(path +
"/.cvmfsautocatalog");
1395 catalog_balancer.
Balance(catalog);
1414 int dirty_catalogs = (wr_catalog->
IsDirty()) ? 1 : 0;
1418 for (CatalogList::const_iterator i = children.begin(), iEnd = children.end();
1426 if (dirty_catalogs > 0)
1427 result->push_back(const_cast<WritableCatalog *>(wr_catalog));
1430 return dirty_catalogs;
1453 const bool stop_for_tweaks)
1466 WritableCatalogList::const_iterator i = catalogs_to_snapshot.begin();
1467 const WritableCatalogList::const_iterator iend = catalogs_to_snapshot.end();
1468 for (; i != iend; ++i) {
1478 (*i)->mountpoint().ToString().c_str());
1481 int64_t catalog_size =
GetFileSize((*i)->database_path());
1482 assert(catalog_size > 0);
1484 if ((*i)->HasParent()) {
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();
1494 root_catalog_info.
revision = (*i)->GetRevision();
1499 spooler_->ProcessCatalog((*i)->database_path());
1504 return root_catalog_info;
bool CompressPath2Null(const string &src, shash::Any *compressed_hash)
uint32_t linkcount() const
int return_code
the return value of the spooler operation
void DetachSubtree(Catalog *catalog)
bool IsExternalFile() const
const Counters & GetCounters() const
void AddChunkedFile(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory, const FileChunkList &file_chunks)
const manifest::Manifest * manifest() const
void set_base_hash(const shash::Any &hash)
ShortString< kDefaultMaxName, 1 > NameString
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
void set_dirty_children(const int count)
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
bool UseLocalCache() const
void set_catalog_hash(const shash::Any &catalog_hash)
NameString GetFileName(const PathString &path)
Catalog * LoadFreeCatalog(const PathString &mountpoint, const shash::Any &hash)
bool IsChunkedFile() const
unsigned nested_kcatalog_limit_
bool MountSubtree(const PathString &path, const Catalog *entry_point, bool can_listing, Catalog **leaf_catalog)
~WritableCatalogManager()
void RemoveDirectory(const std::string &directory_path)
uint32_t GetMaxLinkId() const
void UpdateNestedCatalog(const std::string &path, const shash::Any &hash, const uint64_t size, const DeltaCounters &child_counters)
FILE * CreateTempFile(const std::string &path_prefix, const int mode, const char *open_flags, std::string *final_path)
WritableCatalog * GetHostingCatalog(const std::string &path)
void Assign(const char *chars, const unsigned length)
void set_linkcount(const uint32_t linkcount)
void set_is_nested_catalog_root(const bool val)
void FixWeight(WritableCatalog *catalog)
std::string ToStringWithSuffix() const
void Balance(catalog_t *catalog)
bool LookupPath(const PathString &path, DirectoryEntry *dirent) const
void UpdateLastModified()
void ScheduleCatalogProcessing(WritableCatalog *catalog)
std::map< std::string, WritableCatalog * > catalog_processing_map_
std::string CreateTempPath(const std::string &path_prefix, const int mode)
void DetachCatalog(Catalog *catalog)
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)
void InsertNestedCatalog(const std::string &mountpoint, Catalog *attached_reference, const shash::Any content_hash, const uint64_t size)
void TouchEntry(const DirectoryEntryBase &entry, const XattrList &xattrs, const shash::Md5 &path_hash)
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
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
upload::Spooler * spooler
void SetPreviousRevision(const shash::Any &hash)
pthread_mutex_t * catalog_processing_lock_
Catalog * FindChild(const PathString &mountpoint) const
std::vector< WritableCatalog * > WritableCatalogList
shash::Any checksum() const
void CloneTreeImpl(const PathString &source_dir, const std::string &dest_parent_dir, const NameString &dest_name)
const unsigned kLookupDefault
bool CopyPath2File(const std::string &src, FILE *fdest)
void AddAsSubtree(DeltaCounters *delta) const
bool IsNestedCatalogMountpoint() const
bool IsTransitionPoint(const std::string &mountpoint)
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
void TouchDirectory(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &directory_path)
uint64_t GetNumEntries() const
uint64_t GetRevision() const
void AddFileChunk(const std::string &entry_path, const FileChunk &chunk)
bool IsAutogenerated() const
std::vector< DirectoryEntry > DirectoryEntryList
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)
const unsigned max_weight_
void ActivateCatalog(Catalog *catalog)
std::string local_path
the local_path previously given as input
bool LookupXattrsPath(const PathString &path, XattrList *xattrs) const
void ShrinkHardlinkGroup(const std::string &remove_path)
bool CopyCatalogToLocalCache(const upload::SpoolerResult &result)
bool GetModifiedCatalogLeafsRecursively(Catalog *catalog, WritableCatalogList *result) const
const char kSuffixCatalog
pthread_mutex_t * sync_lock_
unsigned root_kcatalog_limit_
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))
CatalogInfo SnapshotCatalogsSerialized(const bool stop_for_tweaks)
CatalogInfo SnapshotCatalogs(const bool stop_for_tweaks)
PathString mountpoint() const
static const inode_t kInvalidInode
void Append(const char *chars, const unsigned length)
void TakeDatabaseFileOwnership()
bool SetVOMSAuthz(const std::string &voms_authz)
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
void Clone(const std::string from, const std::string to)
std::vector< Catalog * > CatalogList
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)
Catalog * GetRootCatalog() const
upload::Spooler * spooler_
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)
const unsigned min_weight_
void set_ttl(const uint32_t ttl)
shash::Algorithms GetHashAlgorithm() const
std::string MakePathWithoutSuffix() const
std::string ToString() const
std::vector< NestedCatalog > NestedCatalogList
bool ListFileChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks)
void Partition(WritableCatalog *new_nested_catalog)
ShortString< kDefaultMaxPath, 0 > PathString
void SetRevision(const uint64_t new_revision)
void set_hardlink_group(const uint32_t group)
PathString GetParentPath(const PathString &path)
WritableCatalog * GetWritableParent() const
void set_catalog_size(const uint64_t catalog_size)
DeltaCounters delta_counters_
bool FindNested(const PathString &mountpoint, shash::Any *hash, uint64_t *size) const
void VacuumDatabaseIfNecessary()
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)
std::vector< DirectoryEntryBase > DirectoryEntryBaseList
void set_has_alt_catalog_path(const bool &has_alt_path)
unsigned GetLength() const
CatalogT * FindCatalog(const PathString &path) const
std::string MakePath() const
void AddFile(const DirectoryEntryBase &entry, const XattrList &xattrs, const std::string &parent_directory)
Future< CatalogInfo > * root_catalog_info
const char * c_str() const
void SwapNestedCatalog(const string &mountpoint, const shash::Any &new_hash, const uint64_t new_size)
void RemoveEntry(const std::string &entry_path)
const char * GetChars() const
virtual bool IsWritable() const
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)
void PrecalculateListings()
int DecrementDirtyChildren()
const Item * AtPtr(const size_t index) const
void set_is_nested_catalog_mountpoint(const bool val)
bool CompressPath2Path(const string &src, const string &dest)
void CreateNestedCatalog(const std::string &mountpoint)
void UpdateEntry(const DirectoryEntry &entry, const shash::Md5 &path_hash)
static DerivedT * Create(const std::string &filename)
void GetModifiedCatalogs(WritableCatalogList *result) const
unsigned file_mbyte_limit_
void set_root_path(const std::string &root_path)
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
const shash::Any & base_hash() const