7 #define __STDC_FORMAT_MACROS
42 return *
const_cast<uint32_t *
>(
43 reinterpret_cast<const uint32_t *
>(key.
digest) + 1);
47 namespace swissknife {
49 CommandCheck::CommandCheck()
50 : check_chunks_(false)
51 , no_duplicates_map_(false)
59 const bool compare_names,
60 const bool is_transition_point)
65 if (diffs == Difference::kIdentical) {
71 if (is_transition_point &&
72 (diffs ^ Difference::kNestedCatalogTransitionFlags) == 0) {
78 if (diffs & Difference::kName) {
84 if (diffs & Difference::kLinkcount) {
89 if (diffs & Difference::kHardlinkGroup) {
94 if (diffs & Difference::kSize) {
99 if (diffs & Difference::kMode) {
104 if (diffs & Difference::kMtime) {
109 if (diffs & Difference::kChecksum) {
114 if (diffs & Difference::kSymlink) {
119 if (diffs & Difference::kExternalFileFlag) {
125 if (diffs & Difference::kHasXattrsFlag) {
131 if (!is_transition_point) {
132 if (diffs & Difference::kUid) {
137 if (diffs & Difference::kGid) {
155 catalog::Counters::FieldsMap::const_iterator i = map_a.begin();
156 catalog::Counters::FieldsMap::const_iterator iend = map_a.end();
157 for (; i != iend; ++i) {
158 catalog::Counters::FieldsMap::const_iterator comp = map_b.find(i->first);
159 assert(comp != map_b.end());
161 if (*(i->second) != *(comp->second)) {
163 "catalog statistics mismatch: %s (expected: %" PRIu64
" / "
164 "in catalog: %" PRIu64
")",
165 comp->first.c_str(), *(i->second), *(comp->second));
228 string reflog_path =
FetchPath(
".cvmfsreflog");
231 if (computed_hash != reflog_hash) {
233 "The .cvmfsreflog has unexpected content hash %s (expected %s)",
235 unlink(reflog_path.c_str());
245 "failed to find catalog root hash %s in .cvmfsreflog",
252 "failed to find certificate hash %s in .cvmfsreflog",
261 "failed to find tag database's hash %s in .cvmfsreflog",
270 "failed to find meta info hash %s in .cvmfsreflog",
285 vector<history::History::Tag> tags;
286 retval = history->
List(&tags);
291 vector<history::History::Branch> branches;
300 map<string, uint64_t> initial_revisions;
302 for (
unsigned i = 0; i < branches.size(); ++i) {
303 if (!sanitizer.
IsValid(branches[i].branch)) {
305 branches[i].branch.c_str());
308 initial_revisions[branches[i].branch] = branches[i].initial_revision;
311 set<string> used_branches;
313 for (
unsigned i = 0; i < tags.size(); ++i) {
314 used_branches.insert(tags[i].branch);
315 const map<string, uint64_t>::const_iterator iter =
316 initial_revisions.find(tags[i].branch);
317 if (iter == initial_revisions.end()) {
319 tags[i].branch.c_str(), tags[i].name.c_str());
322 if (tags[i].revision < iter->second) {
324 " of tag %s", tags[i].revision, tags[i].name.c_str());
330 if (used_branches.size() != branches.size()) {
349 set<PathString> *bind_mountpoints)
354 if (!catalog->
LookupPath(path, &this_directory)) {
365 uint32_t num_subdirs = 0;
367 typedef map< uint32_t, vector<catalog::DirectoryEntry> > HardlinkMap;
368 HardlinkMap hardlinks;
369 bool found_nested_marker =
false;
371 for (
unsigned i = 0; i < entries.size(); ++i) {
374 const bool entry_needs_check =
375 !entries[i].checksum().IsNull() && !entries[i].IsExternalFile() &&
384 full_path.
Append(entries[i].name().GetChars(),
385 entries[i].name().GetLength());
387 full_path.
c_str(), entry_needs_check);
391 if (entries[i].name().IsEmpty()) {
398 if (entries[i].name() ==
NameString(
string(
".cvmfscatalog"))) {
401 "found abandoned nested catalog marker at %s",
405 found_nested_marker =
true;
409 if (entries[i].IsRegular() && !entries[i].IsChunkedFile() &&
410 entries[i].checksum().IsNull())
413 "regular file pointing to zero-hash: '%s'", full_path.
c_str());
420 string chunk_path =
"data/" + entries[i].checksum().MakePath();
421 if (entries[i].IsDirectory())
423 if (!
Exists(chunk_path)) {
425 entries[i].checksum().ToString().c_str(), full_path.
c_str());
431 if ((entries[i].linkcount() > 1) && !entries[i].IsDirectory()) {
432 if (entries[i].hardlink_group() == 0) {
437 HardlinkMap::iterator hardlink_group =
438 hardlinks.find(entries[i].hardlink_group());
439 if (hardlink_group == hardlinks.end()) {
440 hardlinks[entries[i].hardlink_group()];
441 hardlinks[entries[i].hardlink_group()].push_back(entries[i]);
443 if (!
CompareEntries(entries[i], (hardlink_group->second)[0],
false)) {
448 hardlink_group->second.push_back(entries[i]);
454 if (entries[i].linkcount() == 0) {
456 entries[i].name().c_str());
461 if (!entries[i].IsRegular()) {
462 if (entries[i].IsDirectIo()) {
468 if (entries[i].IsDirectory()) {
469 computed_counters->
self.directories++;
478 if (entries[i].hardlink_group() != 0) {
483 if (entries[i].IsNestedCatalogMountpoint() ||
484 entries[i].IsBindMountpoint())
487 if (entries[i].IsNestedCatalogMountpoint())
488 computed_counters->
self.nested_catalogs++;
492 if (!catalog->
FindNested(mountpoint, &tmp, &tmp2)) {
500 if (catalog->
ListingPath(full_path, &nested_entries) &&
501 !nested_entries.empty()) {
508 if (entries[i].IsBindMountpoint()) {
509 bind_mountpoints->insert(full_path);
510 if (entries[i].IsNestedCatalogMountpoint()) {
512 "bind mountpoint and nested mountpoint mutually exclusive"
513 " at %s.", full_path.
c_str());
519 if (!
Find(catalog, full_path, computed_counters, bind_mountpoints))
522 }
else if (entries[i].IsLink()) {
523 computed_counters->
self.symlinks++;
525 if (!entries[i].checksum().IsNull()) {
531 if (entries[i].
size() != entries[i].symlink().GetLength()) {
533 "expected %u, got %lu", full_path.
c_str(),
534 entries[i].symlink().GetLength(), entries[i].size());
537 }
else if (entries[i].IsRegular()) {
538 computed_counters->
self.regular_files++;
539 computed_counters->
self.file_size += entries[i].size();
540 }
else if (entries[i].IsSpecial()) {
541 computed_counters->
self.specials++;
543 if (entries[i].
size() != 0) {
545 "unexpected non-zero special file size %s",
550 if (!entries[i].checksum().IsNull()) {
556 if (entries[i].symlink().GetLength() > 0) {
558 "special file with non-zero symlink at %s", full_path.
c_str());
567 if (entries[i].HasXattrs()) {
568 computed_counters->
self.xattrs++;
571 if (entries[i].IsExternalFile()) {
572 computed_counters->
self.externals++;
573 computed_counters->
self.external_file_size += entries[i].size();
574 if (!entries[i].IsRegular()) {
576 "only regular files can be external: %s", full_path.
c_str());
582 if (entries[i].IsChunkedFile()) {
584 catalog->
ListPathChunks(full_path, entries[i].hash_algorithm(), &chunks);
586 computed_counters->
self.chunked_files++;
587 computed_counters->
self.chunked_file_size += entries[i].size();
588 computed_counters->
self.file_chunks += chunks.
size();
591 if (chunks.
size() == 0) {
597 size_t aggregated_file_size = 0;
598 off_t next_offset = 0;
600 for (
unsigned j = 0; j < chunks.
size(); ++j) {
603 if (next_offset != this_chunk.
offset()) {
608 next_offset = this_chunk.
offset() + this_chunk.
size();
609 aggregated_file_size += this_chunk.
size();
616 bool chunk_needs_check =
true;
620 chunk_needs_check =
false;
622 if (chunk_needs_check) {
623 const string chunk_path =
"data/" + chunk_hash.
MakePath();
624 if (!
Exists(chunk_path)) {
626 "offset: %ld | size: %lu) missing",
638 if (aggregated_file_size != entries[i].
size()) {
640 "mismatch. Calculated %zu bytes | %lu "
643 aggregated_file_size,
652 !found_nested_marker)
660 if (this_directory.
linkcount() != num_subdirs + 2) {
662 "expected %u, got %u",
668 for (HardlinkMap::const_iterator i = hardlinks.begin(),
669 iEnd = hardlinks.end(); i != iEnd; ++i)
671 if (i->second[0].linkcount() != i->second.size()) {
673 "expected %lu, got %u",
674 (path.
ToString() +
"/" + i->second[0].name().ToString()).c_str(),
675 i->second.size(), i->second[0].linkcount());
685 string source =
"data/" + catalog_hash.
MakePath();
695 catalog_hash.
ToString().c_str(), retval);
704 string source =
"data/" + catalog_hash.
MakePath();
715 const uint64_t catalog_size) {
722 if (tmp_file ==
"") {
731 if (catalog_file_size <= 0) {
734 catalog_hash.
ToString().c_str(), path.c_str(), tmp_file.c_str() );
735 assert(catalog_file_size > 0);
737 unlink(tmp_file.c_str());
739 if ((catalog_size > 0) && (uint64_t(catalog_file_size) != catalog_size)) {
741 "expected %" PRIu64
", got %" PRIu64,
742 catalog_size, catalog_file_size);
753 uint64_t *root_size) {
755 if (current_catalog == NULL) {
759 typedef vector<string> Tokens;
760 const Tokens path_tokens =
SplitString(subtree_path,
'/');
762 string current_path =
"";
764 Tokens::const_iterator i = path_tokens.begin();
765 Tokens::const_iterator iend = path_tokens.end();
766 for (; i != iend; ++i) {
771 current_path +=
"/" + *i;
775 delete current_catalog;
777 if (current_path.length() < subtree_path.length()) {
778 current_catalog =
FetchCatalog(current_path, *root_hash);
779 if (current_catalog == NULL) {
796 const uint64_t catalog_size,
797 const bool is_nested_catalog,
802 catalog_hash.
ToString().c_str(), path ==
"" ?
"/" : path.c_str());
807 if (catalog == NULL) {
815 if (catalog->root_prefix() !=
PathString(path.data(), path.length())) {
817 "expected %s, got %s",
818 path.c_str(), catalog->root_prefix().c_str());
824 if (!catalog->LookupPath(catalog->root_prefix(), &root_entry)) {
834 if (is_nested_catalog) {
835 if (transition_point != NULL &&
838 "transition point and root entry differ (%s)", path.c_str());
843 "nested catalog root expected but not found (%s)", path.c_str());
849 "nested catalog root found but not expected (%s)", path.c_str());
855 set<PathString> bind_mountpoints;
857 computed_counters, &bind_mountpoints))
864 computed_counters->
self.xattrs++;
865 const uint64_t num_found_entries =
867 computed_counters->
self.regular_files +
868 computed_counters->
self.symlinks +
869 computed_counters->
self.specials +
870 computed_counters->
self.directories;
871 if (num_found_entries != catalog->GetNumEntries()) {
873 "expected %" PRIu64
", got %" PRIu64,
874 catalog->GetNumEntries(), num_found_entries);
880 catalog->ListNestedCatalogs();
882 catalog->ListOwnNestedCatalogs();
883 if (own_nested_catalogs.size() !=
884 static_cast<uint64_t
>(computed_counters->
self.nested_catalogs))
887 " expected %lu, got %lu", computed_counters->
self.nested_catalogs,
888 own_nested_catalogs.size());
891 set<PathString> nested_catalog_paths;
892 for (catalog::Catalog::NestedCatalogList::const_iterator i =
893 nested_catalogs.begin(), iEnd = nested_catalogs.end(); i != iEnd; ++i)
895 nested_catalog_paths.insert(i->mountpoint);
897 if (nested_catalog_paths.size() != nested_catalogs.size()) {
899 "duplicates among nested catalogs and bind mountpoints");
903 for (catalog::Catalog::NestedCatalogList::const_iterator i =
904 nested_catalogs.begin(), iEnd = nested_catalogs.end(); i != iEnd; ++i)
906 if (bind_mountpoints.find(i->mountpoint) != bind_mountpoints.end()) {
908 PathString mountpoint(
"/" + i->mountpoint.ToString().substr(1));
909 if (!catalog->LookupPath(mountpoint, &bind_mountpoint)) {
915 i->mountpoint.c_str());
919 if (!catalog->LookupPath(i->mountpoint, &nested_transition_point)) {
921 i->mountpoint.c_str());
925 const bool is_nested =
true;
926 if (!
InspectTree(i->mountpoint.ToString(), i->hash, i->size, is_nested,
927 &nested_transition_point, &nested_counters))
935 computed_counters->
self.directories++;
937 compare_counters.
ApplyDelta(*computed_counters);
952 string subtree_path =
"";
953 string pubkey_path =
"";
954 string repo_name =
"";
955 string reflog_chksum_path =
"";
957 temp_directory_ = (args.find(
't') != args.end()) ? *args.find(
't')->second
959 if (args.find(
'n') != args.end())
960 tag_name = *args.find(
'n')->second;
961 if (args.find(
'c') != args.end())
963 if (args.find(
'd') != args.end())
965 if (args.find(
'l') != args.end()) {
974 if (args.find(
'k') != args.end())
975 pubkey_path = *args.find(
'k')->second;
978 if (args.find(
'N') != args.end())
979 repo_name = *args.find(
'N')->second;
982 if (args.find(
's') != args.end())
984 if (args.find(
'R') != args.end())
985 reflog_chksum_path = *args.find(
'R')->second;
992 const bool follow_redirects = (args.count(
'L') > 0);
993 const string proxy = (args.count(
'@') > 0) ? *args.find(
'@')->second :
"";
998 if (pubkey_path.empty() || repo_name.empty()) {
1000 "remote repositories");
1011 bool successful =
true;
1030 if (!manifest->meta_info().IsNull()) {
1036 if (tmp_file ==
"") {
1038 manifest->meta_info().ToString().c_str());
1041 unlink(tmp_file.c_str());
1045 if (!reflog_chksum_path.empty()) {
1051 reflog_hash = manifest->reflog_hash();
1054 if (
Exists(
".cvmfsreflog")) {
1055 if (reflog_hash.
IsNull()) {
1058 ".cvmfsreflog present but no checksum provided, aborting");
1067 if (!reflog_hash.
IsNull()) {
1070 if (!reflog_chksum_path.empty()) {
1072 "local reflog checksum set but reflog itself is missing, "
1081 if (!manifest->history().IsNull()) {
1087 if (tmp_file ==
"") {
1089 manifest->history().ToString().c_str());
1095 manifest->history().ToString().c_str());
1098 tag_db->TakeDatabaseFileOwnership();
1102 if (manifest->has_alt_catalog_path()) {
1103 if (!
Exists(manifest->certificate().MakeAlternativePath())) {
1105 "failed to find alternative certificate link %s",
1106 manifest->certificate().MakeAlternativePath().c_str());
1109 if (!
Exists(manifest->catalog_hash().MakeAlternativePath())) {
1111 "failed to find alternative catalog link %s",
1112 manifest->catalog_hash().MakeAlternativePath().c_str());
1117 shash::Any root_hash = manifest->catalog_hash();
1118 uint64_t root_size = manifest->catalog_size();
1119 if (tag_name !=
"") {
1125 const bool retval = tag_db->GetByName(tag_name, &tag);
1131 root_size = tag.
size;
1136 const bool is_nested_catalog = (!subtree_path.empty());
1141 subtree_path.c_str());
1152 &computed_counters) && successful;
uint32_t linkcount() const
void SetLogVerbosity(const LogLevels max_level)
void TakeDatabaseFileOwnership()
bool IsExternalFile() const
bool ContainsHistory(const shash::Any &history) const
bool InspectHistory(history::History *history)
Differences CompareTo(const DirectoryEntry &other) const
const manifest::Manifest * manifest() const
ShortString< kDefaultMaxName, 1 > NameString
Item At(const size_t index) const
static SqliteHistory * Open(const std::string &file_name)
bool ListPathChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks) const
const int kDefaultFileMode
static bool ReadChecksum(const std::string &path, shash::Any *checksum)
FILE * CreateTempFile(const std::string &path_prefix, const int mode, const char *open_flags, std::string *final_path)
string JoinStrings(const vector< string > &strings, const string &joint)
std::string ToString(const bool with_suffix=false) const
const history::History * history() const
std::string ToStringWithSuffix() const
bool LookupPath(const PathString &path, DirectoryEntry *dirent) const
bool CompareEntries(const catalog::DirectoryEntry &a, const catalog::DirectoryEntry &b, const bool compare_names, const bool is_transition_point=false)
void ApplyDelta(const DeltaCounters &delta)
bool IsHttpUrl(const std::string &path)
bool ListingPath(const PathString &path, DirectoryEntryList *listing, const bool expand_symlink=true) const
const char kSuffixMicroCatalog
const shash::Any & content_hash() const
catalog::Catalog * FetchCatalog(const std::string &path, const shash::Any &catalog_hash, const uint64_t catalog_size=0)
std::map< std::string, const Counters_t * > FieldsMap
assert((mem||(size==0))&&"Out Of Memory")
bool InspectReflog(const shash::Any &reflog_hash, manifest::Manifest *manifest)
std::string temp_directory_
shash::Any checksum() const
static uint32_t hasher_any(const shash::Any &key)
unsigned int mode() const
unsigned char digest[digest_size_]
bool Find(const catalog::Catalog *catalog, const PathString &path, catalog::DeltaCounters *computed_counters, std::set< PathString > *bind_mountpoints)
std::string repo_base_path_
bool ContainsMetainfo(const shash::Any &metainfo) const
bool CopyPath2File(const std::string &src, FILE *fdest)
FieldsMap GetFieldsMap() const
bool SymlinkExists(const std::string &path)
static Reflog * Open(const std::string &database_path)
bool IsNestedCatalogRoot() const
bool FileExists(const std::string &path)
std::string DownloadPiece(const shash::Any catalog_hash)
std::vector< DirectoryEntry > DirectoryEntryList
int Main(const ArgumentList &args)
virtual bool ListBranches(std::vector< Branch > *branches) const =0
std::string DecompressPiece(const shash::Any catalog_hash)
virtual bool List(std::vector< Tag > *tags) const =0
vector< string > SplitString(const string &str, char delim)
bool Exists(const std::string &file)
shash::Any certificate() const
void PopulateToParent(DeltaCounters *parent) const
std::string FetchPath(const std::string &path)
LinkString symlink() const
void Insert(const Key &key, const Value &value)
PathString mountpoint() const
static void HashDatabase(const std::string &database_path, shash::Any *hash_reflog)
shash::Any catalog_hash() const
void Append(const char *chars, const unsigned length)
manifest::Reflog * reflog
SmallHashDynamic< shash::Any, char > duplicates_map_
bool FindSubtreeRootCatalog(const std::string &subtree_path, shash::Any *root_hash, uint64_t *root_size)
bool DirectoryExists(const std::string &path)
bool CompareCounters(const catalog::Counters &a, const catalog::Counters &b)
bool Contains(const Key &key) const
std::string ToString() const
std::vector< NestedCatalog > NestedCatalogList
bool ContainsCatalog(const shash::Any &catalog) const
uint64_t String2Uint64(const string &value)
std::map< char, SharedPtr< std::string > > ArgumentList
ShortString< kDefaultMaxPath, 0 > PathString
Failures Fetch(JobInfo *info)
shash::Any history() const
bool InspectTree(const std::string &path, const shash::Any &catalog_hash, const uint64_t catalog_size, const bool is_nested_catalog, const catalog::DirectoryEntry *transition_point, catalog::DeltaCounters *computed_counters)
static Catalog * AttachFreely(const std::string &imaginary_mountpoint, const std::string &file, const shash::Any &catalog_hash, Catalog *parent=NULL, const bool is_nested=false)
bool FindNested(const PathString &mountpoint, shash::Any *hash, uint64_t *size) const
bool DecompressPath2Path(const string &src, const string &dest)
int64_t GetFileSize(const std::string &path)
std::string MakePath() const
std::string MakeCanonicalPath(const std::string &path)
void Init(uint32_t expected_size, Key empty, uint32_t(*hasher)(const Key &key))
const char * c_str() const
uint32_t hardlink_group() const
bool ContainsCertificate(const shash::Any &certificate) const
std::vector< std::string > FindFilesBySuffix(const std::string &dir, const std::string &suffix)
shash::Any meta_info() const
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)