7 #define __STDC_FORMAT_MACROS
42 return *
const_cast<uint32_t *
>(
reinterpret_cast<const uint32_t *
>(key.
digest)
47 namespace swissknife {
49 CommandCheck::CommandCheck()
50 : check_chunks_(false), no_duplicates_map_(false), is_remote_(false) {
57 const bool compare_names,
58 const bool is_transition_point) {
62 if (diffs == Difference::kIdentical) {
68 if (is_transition_point
69 && (diffs ^ Difference::kNestedCatalogTransitionFlags) == 0) {
75 if (diffs & Difference::kName) {
81 if (diffs & Difference::kLinkcount) {
86 if (diffs & Difference::kHardlinkGroup) {
91 if (diffs & Difference::kSize) {
96 if (diffs & Difference::kMode) {
101 if (diffs & Difference::kMtime) {
106 if (diffs & Difference::kChecksum) {
111 if (diffs & Difference::kSymlink) {
116 if (diffs & Difference::kExternalFileFlag) {
118 "external file flag differs: %d / %d "
124 if (diffs & Difference::kHasXattrsFlag) {
126 "extended attributes differ: %d / %d "
131 if (!is_transition_point) {
132 if (diffs & Difference::kUid) {
137 if (diffs & Difference::kGid) {
154 catalog::Counters::FieldsMap::const_iterator i = map_a.begin();
155 const catalog::Counters::FieldsMap::const_iterator iend = map_a.end();
156 for (; i != iend; ++i) {
157 const catalog::Counters::FieldsMap::const_iterator comp =
158 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));
225 const string reflog_path =
FetchPath(
".cvmfsreflog");
228 if (computed_hash != reflog_hash) {
230 "The .cvmfsreflog has unexpected content hash %s (expected %s)",
232 unlink(reflog_path.c_str());
242 "failed to find catalog root hash %s in .cvmfsreflog",
249 "failed to find certificate hash %s in .cvmfsreflog",
257 "failed to find tag database's hash %s in .cvmfsreflog",
265 "failed to find meta info hash %s in .cvmfsreflog",
280 vector<history::History::Tag> tags;
281 retval = history->
List(&tags);
286 vector<history::History::Branch> branches;
295 map<string, uint64_t> initial_revisions;
297 for (
unsigned i = 0; i < branches.size(); ++i) {
298 if (!sanitizer.
IsValid(branches[i].branch)) {
300 branches[i].branch.c_str());
303 initial_revisions[branches[i].branch] = branches[i].initial_revision;
306 set<string> used_branches;
308 for (
unsigned i = 0; i < tags.size(); ++i) {
309 used_branches.insert(tags[i].branch);
310 const map<string, uint64_t>::const_iterator iter = initial_revisions.find(
312 if (iter == initial_revisions.end()) {
314 tags[i].branch.c_str(), tags[i].name.c_str());
317 if (tags[i].revision < iter->second) {
319 "invalid revision %" PRIu64
" of tag %s", tags[i].revision,
320 tags[i].name.c_str());
326 if (used_branches.size() != branches.size()) {
345 set<PathString> *bind_mountpoints) {
349 if (!catalog->
LookupPath(path, &this_directory)) {
358 uint32_t num_subdirs = 0;
360 typedef map<uint32_t, vector<catalog::DirectoryEntry> > HardlinkMap;
361 HardlinkMap hardlinks;
362 bool found_nested_marker =
false;
364 for (
unsigned i = 0; i < entries.size(); ++i) {
367 const bool entry_needs_check = !entries[i].checksum().IsNull()
368 && !entries[i].IsExternalFile() &&
373 entries[i].checksum()));
379 full_path.
Append(entries[i].name().GetChars(),
380 entries[i].name().GetLength());
382 full_path.
c_str(), entry_needs_check);
386 if (entries[i].name().IsEmpty()) {
392 if (entries[i].name() ==
NameString(
string(
".cvmfscatalog"))) {
395 "found abandoned nested catalog marker at %s",
399 found_nested_marker =
true;
403 if (entries[i].IsRegular() && !entries[i].IsChunkedFile()
404 && entries[i].checksum().IsNull()) {
406 "regular file pointing to zero-hash: '%s'", full_path.
c_str());
412 string chunk_path =
"data/" + entries[i].checksum().MakePath();
413 if (entries[i].IsDirectory())
415 if (!
Exists(chunk_path)) {
417 entries[i].checksum().ToString().c_str(), full_path.
c_str());
423 if ((entries[i].linkcount() > 1) && !entries[i].IsDirectory()) {
424 if (entries[i].hardlink_group() == 0) {
429 const HardlinkMap::iterator hardlink_group =
430 hardlinks.find(entries[i].hardlink_group());
431 if (hardlink_group == hardlinks.end()) {
432 hardlinks[entries[i].hardlink_group()];
433 hardlinks[entries[i].hardlink_group()].push_back(entries[i]);
435 if (!
CompareEntries(entries[i], (hardlink_group->second)[0],
false)) {
440 hardlink_group->second.push_back(entries[i]);
446 if (entries[i].linkcount() == 0) {
448 entries[i].name().c_str());
453 if (!entries[i].IsRegular()) {
454 if (entries[i].IsDirectIo()) {
460 if (entries[i].IsDirectory()) {
461 computed_counters->
self.directories++;
470 if (entries[i].hardlink_group() != 0) {
475 if (entries[i].IsNestedCatalogMountpoint()
476 || entries[i].IsBindMountpoint()) {
478 if (entries[i].IsNestedCatalogMountpoint())
479 computed_counters->
self.nested_catalogs++;
483 if (!catalog->
FindNested(mountpoint, &tmp, &tmp2)) {
491 if (catalog->
ListingPath(full_path, &nested_entries)
492 && !nested_entries.empty()) {
494 "non-empty nested catalog mountpoint "
500 if (entries[i].IsBindMountpoint()) {
501 bind_mountpoints->insert(full_path);
502 if (entries[i].IsNestedCatalogMountpoint()) {
504 "bind mountpoint and nested mountpoint mutually exclusive"
512 if (!
Find(catalog, full_path, computed_counters, bind_mountpoints))
515 }
else if (entries[i].IsLink()) {
516 computed_counters->
self.symlinks++;
518 if (!entries[i].checksum().IsNull()) {
524 if (entries[i].
size() != entries[i].symlink().GetLength()) {
526 "wrong symbolic link size for %s; "
527 "expected %u, got %lu",
528 full_path.
c_str(), entries[i].symlink().GetLength(),
532 }
else if (entries[i].IsRegular()) {
533 computed_counters->
self.regular_files++;
534 computed_counters->
self.file_size += entries[i].size();
535 }
else if (entries[i].IsSpecial()) {
536 computed_counters->
self.specials++;
538 if (entries[i].
size() != 0) {
540 "unexpected non-zero special file size %s", full_path.
c_str());
544 if (!entries[i].checksum().IsNull()) {
550 if (entries[i].symlink().GetLength() > 0) {
552 "special file with non-zero symlink at %s", full_path.
c_str());
561 if (entries[i].HasXattrs()) {
562 computed_counters->
self.xattrs++;
565 if (entries[i].IsExternalFile()) {
566 computed_counters->
self.externals++;
567 computed_counters->
self.external_file_size += entries[i].size();
568 if (!entries[i].IsRegular()) {
570 "only regular files can be external: %s", full_path.
c_str());
576 if (entries[i].IsChunkedFile()) {
578 catalog->
ListPathChunks(full_path, entries[i].hash_algorithm(), &chunks);
580 computed_counters->
self.chunked_files++;
581 computed_counters->
self.chunked_file_size += entries[i].size();
582 computed_counters->
self.file_chunks += chunks.
size();
585 if (chunks.
size() == 0) {
591 size_t aggregated_file_size = 0;
592 off_t next_offset = 0;
594 for (
unsigned j = 0; j < chunks.
size(); ++j) {
597 if (next_offset != this_chunk.
offset()) {
602 next_offset = this_chunk.
offset() + this_chunk.
size();
603 aggregated_file_size += this_chunk.
size();
610 bool chunk_needs_check =
true;
614 chunk_needs_check =
false;
616 if (chunk_needs_check) {
617 const string chunk_path =
"data/" + chunk_hash.
MakePath();
618 if (!
Exists(chunk_path)) {
620 "partial data chunk %s (%s -> "
621 "offset: %ld | size: %lu) missing",
632 if (aggregated_file_size != entries[i].
size()) {
634 "chunks of file %s produce a size "
635 "mismatch. Calculated %zu bytes | %lu "
637 full_path.
c_str(), aggregated_file_size, entries[i].size());
645 && !found_nested_marker) {
652 if (this_directory.
linkcount() != num_subdirs + 2) {
654 "wrong linkcount for %s; "
655 "expected %u, got %u",
661 for (HardlinkMap::const_iterator i = hardlinks.begin(),
662 iEnd = hardlinks.end();
665 if (i->second[0].linkcount() != i->second.size()) {
667 "hardlink linkcount wrong for %s, "
668 "expected %lu, got %u",
669 (path.
ToString() +
"/" + i->second[0].name().ToString()).c_str(),
670 i->second.size(), i->second[0].linkcount());
691 catalog_hash.
ToString().c_str(), retval);
711 const uint64_t catalog_size) {
718 if (tmp_file ==
"") {
726 const int64_t catalog_file_size =
GetFileSize(tmp_file);
727 if (catalog_file_size <= 0) {
729 catalog_hash.
ToString().c_str(), path.c_str(), tmp_file.c_str());
730 assert(catalog_file_size > 0);
732 unlink(tmp_file.c_str());
734 if ((catalog_size > 0) && (uint64_t(catalog_file_size) != catalog_size)) {
736 "catalog file size mismatch, "
737 "expected %" PRIu64
", got %" PRIu64,
738 catalog_size, catalog_file_size);
749 uint64_t *root_size) {
751 if (current_catalog == NULL) {
755 typedef vector<string> Tokens;
756 const Tokens path_tokens =
SplitString(subtree_path,
'/');
758 string current_path =
"";
760 Tokens::const_iterator i = path_tokens.begin();
761 const Tokens::const_iterator iend = path_tokens.end();
762 for (; i != iend; ++i) {
767 current_path +=
"/" + *i;
769 PathString(current_path), root_hash, root_size)) {
770 delete current_catalog;
772 if (current_path.length() < subtree_path.length()) {
773 current_catalog =
FetchCatalog(current_path, *root_hash);
774 if (current_catalog == NULL) {
791 const uint64_t catalog_size,
792 const bool is_nested_catalog,
796 catalog_hash.
ToString().c_str(), path ==
"" ?
"/" : path.c_str());
799 path, catalog_hash, catalog_size);
800 if (catalog == NULL) {
808 if (catalog->root_prefix() !=
PathString(path.data(), path.length())) {
810 "root prefix mismatch; "
811 "expected %s, got %s",
812 path.c_str(), catalog->root_prefix().c_str());
818 if (!catalog->LookupPath(catalog->root_prefix(), &root_entry)) {
828 if (is_nested_catalog) {
829 if (transition_point != NULL
832 "transition point and root entry differ (%s)", path.c_str());
837 "nested catalog root expected but not found (%s)", path.c_str());
843 "nested catalog root found but not expected (%s)", path.c_str());
849 set<PathString> bind_mountpoints;
850 if (!
Find(catalog,
PathString(path.data(), path.length()), computed_counters,
851 &bind_mountpoints)) {
857 computed_counters->
self.xattrs++;
858 const uint64_t num_found_entries = 1 + computed_counters->
self.regular_files
859 + computed_counters->
self.symlinks
860 + computed_counters->
self.specials
861 + computed_counters->
self.directories;
862 if (num_found_entries != catalog->GetNumEntries()) {
864 "dangling entries in catalog, "
865 "expected %" PRIu64
", got %" PRIu64,
866 catalog->GetNumEntries(), num_found_entries);
872 &nested_catalogs = catalog->ListNestedCatalogs();
874 own_nested_catalogs = catalog->ListOwnNestedCatalogs();
875 if (own_nested_catalogs.size()
876 !=
static_cast<uint64_t
>(computed_counters->
self.nested_catalogs)) {
878 "number of nested catalogs does not match;"
879 " expected %lu, got %lu",
880 computed_counters->
self.nested_catalogs,
881 own_nested_catalogs.size());
884 set<PathString> nested_catalog_paths;
885 for (catalog::Catalog::NestedCatalogList::const_iterator
886 i = nested_catalogs.begin(),
887 iEnd = nested_catalogs.end();
889 nested_catalog_paths.insert(i->mountpoint);
891 if (nested_catalog_paths.size() != nested_catalogs.size()) {
893 "duplicates among nested catalogs and bind mountpoints");
897 for (catalog::Catalog::NestedCatalogList::const_iterator
898 i = nested_catalogs.begin(),
899 iEnd = nested_catalogs.end();
901 if (bind_mountpoints.find(i->mountpoint) != bind_mountpoints.end()) {
903 const PathString mountpoint(
"/" + i->mountpoint.ToString().substr(1));
904 if (!catalog->LookupPath(mountpoint, &bind_mountpoint)) {
910 i->mountpoint.c_str());
914 if (!catalog->LookupPath(i->mountpoint, &nested_transition_point)) {
916 i->mountpoint.c_str());
920 const bool is_nested =
true;
921 if (!
InspectTree(i->mountpoint.ToString(), i->hash, i->size, is_nested,
922 &nested_transition_point, &nested_counters))
930 computed_counters->
self.directories++;
932 compare_counters.
ApplyDelta(*computed_counters);
947 string subtree_path =
"";
948 string pubkey_path =
"";
949 string repo_name =
"";
950 string reflog_chksum_path =
"";
952 temp_directory_ = (args.find(
't') != args.end()) ? *args.find(
't')->second
954 if (args.find(
'n') != args.end())
955 tag_name = *args.find(
'n')->second;
956 if (args.find(
'c') != args.end())
958 if (args.find(
'd') != args.end())
960 if (args.find(
'l') != args.end()) {
969 if (args.find(
'k') != args.end())
970 pubkey_path = *args.find(
'k')->second;
973 if (args.find(
'N') != args.end())
974 repo_name = *args.find(
'N')->second;
977 if (args.find(
's') != args.end())
979 if (args.find(
'R') != args.end())
980 reflog_chksum_path = *args.find(
'R')->second;
987 const bool follow_redirects = (args.count(
'L') > 0);
988 const string proxy = (args.count(
'@') > 0) ? *args.find(
'@')->second :
"";
993 if (pubkey_path.empty() || repo_name.empty()) {
995 "please provide pubkey and repo name for "
996 "remote repositories");
1007 bool successful =
true;
1026 if (!manifest->meta_info().IsNull()) {
1032 if (tmp_file ==
"") {
1034 manifest->meta_info().ToString().c_str());
1037 unlink(tmp_file.c_str());
1041 if (!reflog_chksum_path.empty()) {
1047 reflog_hash = manifest->reflog_hash();
1050 if (
Exists(
".cvmfsreflog")) {
1051 if (reflog_hash.
IsNull()) {
1054 ".cvmfsreflog present but no checksum provided, aborting");
1063 if (!reflog_hash.
IsNull()) {
1066 if (!reflog_chksum_path.empty()) {
1068 "local reflog checksum set but reflog itself is missing, "
1077 if (!manifest->history().IsNull()) {
1083 if (tmp_file ==
"") {
1085 manifest->history().ToString().c_str());
1091 manifest->history().ToString().c_str());
1094 tag_db->TakeDatabaseFileOwnership();
1098 if (manifest->has_alt_catalog_path()) {
1099 if (!
Exists(manifest->certificate().MakeAlternativePath())) {
1101 "failed to find alternative certificate link %s",
1102 manifest->certificate().MakeAlternativePath().c_str());
1105 if (!
Exists(manifest->catalog_hash().MakeAlternativePath())) {
1107 "failed to find alternative catalog link %s",
1108 manifest->catalog_hash().MakeAlternativePath().c_str());
1113 shash::Any root_hash = manifest->catalog_hash();
1114 uint64_t root_size = manifest->catalog_size();
1115 if (tag_name !=
"") {
1121 const bool retval = tag_db->GetByName(tag_name, &tag);
1127 root_size = tag.
size;
1132 const bool is_nested_catalog = (!subtree_path.empty());
1133 if (is_nested_catalog
1136 subtree_path.c_str());
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)
CVMFS_EXPORT const LogSource source
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
std::map< std::string, const Counters_t * > FieldsMap
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)
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,...)