CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
catalog_diff_tool_impl.h
Go to the documentation of this file.
1 
5 #ifndef CVMFS_CATALOG_DIFF_TOOL_IMPL_H_
6 #define CVMFS_CATALOG_DIFF_TOOL_IMPL_H_
7 
8 #include <algorithm>
9 #include <string>
10 
11 #include "catalog.h"
12 #include "download.h"
13 #include "hash.h"
14 #include "logging.h"
15 #include "util/exception.h"
16 #include "util/posix.h"
17 
18 const uint64_t kLastInode = uint64_t(-1);
19 
21  catalog::DirectoryEntry empty_entry;
22  entry_list->push_back(empty_entry);
23 }
24 
25 inline void AppendLastEntry(catalog::DirectoryEntryList* entry_list) {
26  assert(!entry_list->empty());
27  catalog::DirectoryEntry last_entry;
28  last_entry.set_inode(kLastInode);
29  entry_list->push_back(last_entry);
30 }
31 
32 inline bool IsSmaller(const catalog::DirectoryEntry& a,
33  const catalog::DirectoryEntry& b) {
34  bool a_is_first = (a.inode() == catalog::DirectoryEntryBase::kInvalidInode);
35  bool a_is_last = (a.inode() == kLastInode);
36  bool b_is_first = (b.inode() == catalog::DirectoryEntryBase::kInvalidInode);
37  bool b_is_last = (b.inode() == kLastInode);
38 
39  if (a_is_last || b_is_first) return false;
40  if (a_is_first) return !b_is_first;
41  if (b_is_last) return !a_is_last;
42  return a.name() < b.name();
43 }
44 
45 template <typename RoCatalogMgr>
47  if (needs_setup_) {
48  // Create a temp directory
49  old_raii_temp_dir_ = RaiiTempDir::Create(temp_dir_prefix_);
50  new_raii_temp_dir_ = RaiiTempDir::Create(temp_dir_prefix_);
51 
52  // Old catalog from release manager machine (before lease)
53  old_catalog_mgr_ =
54  OpenCatalogManager(repo_path_, old_raii_temp_dir_->dir(),
55  old_root_hash_, download_manager_, &stats_old_);
56 
57  // New catalog from release manager machine (before lease)
58  new_catalog_mgr_ =
59  OpenCatalogManager(repo_path_, new_raii_temp_dir_->dir(),
60  new_root_hash_, download_manager_, &stats_new_);
61 
62  if (!old_catalog_mgr_.IsValid()) {
63  LogCvmfs(kLogCvmfs, kLogStderr, "Could not open old catalog");
64  return false;
65  }
66 
67  if (!new_catalog_mgr_.IsValid()) {
68  LogCvmfs(kLogCvmfs, kLogStderr, "Could not open new catalog");
69  return false;
70  }
71  }
72 
73  return true;
74 }
75 
76 template <typename RoCatalogMgr>
78  DiffRec(path);
79 
80  return true;
81 }
82 
83 template <typename RoCatalogMgr>
85  const std::string& repo_path, const std::string& temp_dir,
86  const shash::Any& root_hash, download::DownloadManager* download_manager,
87  perf::Statistics* stats) {
88  RoCatalogMgr* mgr = new RoCatalogMgr(root_hash, repo_path, temp_dir,
89  download_manager, stats, true);
90  mgr->Init();
91 
92  return mgr;
93 }
94 
95 template <typename RoCatalogMgr>
97  // Terminate recursion upon reaching an ignored path
98  if (IsIgnoredPath(path)) {
99  assert(!IsReportablePath(path));
100  return;
101  }
102 
103  catalog::DirectoryEntryList old_listing;
104  AppendFirstEntry(&old_listing);
105  old_catalog_mgr_->Listing(path, &old_listing);
106  sort(old_listing.begin(), old_listing.end(), IsSmaller);
107  AppendLastEntry(&old_listing);
108 
109  catalog::DirectoryEntryList new_listing;
110  AppendFirstEntry(&new_listing);
111  new_catalog_mgr_->Listing(path, &new_listing);
112  sort(new_listing.begin(), new_listing.end(), IsSmaller);
113  AppendLastEntry(&new_listing);
114 
115  unsigned i_from = 0, size_from = old_listing.size();
116  unsigned i_to = 0, size_to = new_listing.size();
117  while ((i_from < size_from) || (i_to < size_to)) {
118  catalog::DirectoryEntry old_entry = old_listing[i_from];
119  catalog::DirectoryEntry new_entry = new_listing[i_to];
120 
121  if (old_entry.linkcount() == 0) {
123  "CatalogDiffTool - Entry %s in old catalog has linkcount 0. "
124  "Aborting.",
125  old_entry.name().c_str());
126  }
127  if (new_entry.linkcount() == 0) {
129  "CatalogDiffTool - Entry %s in new catalog has linkcount 0. "
130  "Aborting.",
131  new_entry.name().c_str());
132  }
133 
134  // Skip .cvmfs hidden directory
135  while (old_entry.IsHidden()) old_entry = old_listing[++i_from];
136  while (new_entry.IsHidden()) new_entry = new_listing[++i_to];
137 
138  PathString old_path(path);
139  old_path.Append("/", 1);
140  old_path.Append(old_entry.name().GetChars(), old_entry.name().GetLength());
141  PathString new_path(path);
142  new_path.Append("/", 1);
143  new_path.Append(new_entry.name().GetChars(), new_entry.name().GetLength());
144 
145  XattrList xattrs;
146  if (new_entry.HasXattrs()) {
147  new_catalog_mgr_->LookupXattrs(new_path, &xattrs);
148  }
149 
150  if (IsSmaller(new_entry, old_entry)) {
151  i_to++;
152  if (IsReportablePath(new_path)) {
153  FileChunkList chunks;
154  if (new_entry.IsChunkedFile()) {
155  new_catalog_mgr_->ListFileChunks(new_path, new_entry.hash_algorithm(),
156  &chunks);
157  }
158  ReportAddition(new_path, new_entry, xattrs, chunks);
159  }
160  if (new_entry.IsDirectory()) {
161  DiffRec(new_path);
162  }
163  continue;
164  } else if (IsSmaller(old_entry, new_entry)) {
165  i_from++;
166  if (old_entry.IsDirectory() && !old_entry.IsNestedCatalogMountpoint()) {
167  DiffRec(old_path);
168  }
169  if (IsReportablePath(old_path)) {
170  ReportRemoval(old_path, old_entry);
171  }
172  continue;
173  }
174 
175  assert(old_path == new_path);
176  i_from++;
177  i_to++;
178 
180  old_entry.CompareTo(new_entry);
182  old_entry.IsNestedCatalogMountpoint()) {
183  // Early recursion stop if nested catalogs are identical
184  shash::Any id_nested_from, id_nested_to;
185  id_nested_from = old_catalog_mgr_->GetNestedCatalogHash(old_path);
186  id_nested_to = new_catalog_mgr_->GetNestedCatalogHash(new_path);
187  assert(!id_nested_from.IsNull() && !id_nested_to.IsNull());
188  if (id_nested_from == id_nested_to) continue;
189  }
190 
191  if (IsReportablePath(old_path) &&
193  old_entry.IsNestedCatalogMountpoint())) {
194  // Modified directory entry, or nested catalog with modified hash
195  FileChunkList chunks;
196  if (new_entry.IsChunkedFile()) {
197  new_catalog_mgr_->ListFileChunks(new_path, new_entry.hash_algorithm(),
198  &chunks);
199  }
200  bool recurse =
201  ReportModification(old_path, old_entry, new_entry, xattrs, chunks);
202  if (!recurse) continue;
203  }
204 
205  if (!old_entry.IsDirectory() || !new_entry.IsDirectory()) {
206  if (old_entry.IsDirectory()) {
207  DiffRec(old_path);
208  } else if (new_entry.IsDirectory()) {
209  DiffRec(new_path);
210  }
211  continue;
212  }
213 
214  // Recursion
215  DiffRec(old_path);
216  }
217 }
218 
219 #endif // CVMFS_CATALOG_DIFF_TOOL_IMPL_H_
uint32_t linkcount() const
#define LogCvmfs(source, mask,...)
Definition: logging.h:20
void AppendLastEntry(catalog::DirectoryEntryList *entry_list)
bool IsNull() const
Definition: hash.h:379
Differences CompareTo(const DirectoryEntry &other) const
void AppendFirstEntry(catalog::DirectoryEntryList *entry_list)
void set_inode(const inode_t inode)
bool IsDirectory() const
bool IsHidden() const
bool IsChunkedFile() const
#define PANIC(...)
Definition: exception.h:26
bool IsSmaller(const catalog::DirectoryEntry &a, const catalog::DirectoryEntry &b)
const uint64_t kLastInode
inode_t inode() const
void DiffRec(const PathString &path)
assert((mem||(size==0))&&"Out Of Memory")
bool IsNestedCatalogMountpoint() const
RoCatalogMgr * OpenCatalogManager(const std::string &repo_path, const std::string &temp_dir, const shash::Any &root_hash, download::DownloadManager *download_manager, perf::Statistics *stats)
std::vector< DirectoryEntry > DirectoryEntryList
NameString name() const
bool HasXattrs() const
static RaiiTempDir * Create(const std::string &prefix)
Definition: raii_temp_dir.cc:9
static const inode_t kInvalidInode
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:70
shash::Algorithms hash_algorithm() const
bool Run(const PathString &path)
unsigned GetLength() const
Definition: shortstring.h:104
const char * c_str() const
Definition: shortstring.h:118
const char * GetChars() const
Definition: shortstring.h:96
static const unsigned int kIdentical
unsigned int Differences