CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
catalog_merge_tool_impl.h
Go to the documentation of this file.
1 
5 #ifndef CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
6 #define CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
7 
8 #include <string>
9 
10 #include "catalog.h"
11 #include "crypto/hash.h"
12 #include "lease_path_util.h"
13 #include "manifest.h"
14 #include "options.h"
15 #include "upload.h"
16 #include "util/exception.h"
17 #include "util/logging.h"
18 #include "util/posix.h"
19 #include "util/raii_temp_dir.h"
20 
21 inline PathString MakeRelative(const PathString& path) {
22  std::string rel_path;
23  std::string abs_path = path.ToString();
24  if (abs_path[0] == '/') {
25  rel_path = abs_path.substr(1);
26  } else {
27  rel_path = abs_path;
28  }
29  return PathString(rel_path);
30 }
31 
33  if (entry->linkcount() > 1) {
35  "CatalogMergeTool - Hardlink found: %s. Hardlinks are not "
36  "supported when publishing through repository gateway and "
37  "will be split.", entry->name().c_str());
38  entry->set_linkcount(1);
39  }
40 }
41 
42 inline void AbortIfHardlinked(const catalog::DirectoryEntry& entry) {
43  if (entry.linkcount() > 1) {
45  "CatalogMergeTool - Removal of file %s with linkcount > 1 is "
46  "not supported. Aborting",
47  entry.name().c_str());
48  }
49 }
50 
51 namespace receiver {
52 
53 template <typename RwCatalogMgr, typename RoCatalogMgr>
55  const Params& params, std::string* new_manifest_path, shash::Any* new_manifest_hash, uint64_t *final_rev) {
57  perf::StatisticsTemplate stats_tmpl("publish", statistics_);
58  counters_ = new perf::FsCounters(stats_tmpl);
59 
60  UniquePtr<RaiiTempDir> raii_temp_dir(RaiiTempDir::Create(temp_dir_prefix_));
61  if (needs_setup_) {
62  upload::SpoolerDefinition definition(
63  params.spooler_configuration, params.hash_alg, params.compression_alg,
65  params.min_chunk_size, params.avg_chunk_size, params.max_chunk_size,
66  "dummy_token", "dummy_key");
67  spooler = upload::Spooler::Construct(definition, &stats_tmpl);
68  const std::string temp_dir = raii_temp_dir->dir();
69  output_catalog_mgr_ = new RwCatalogMgr(
70  manifest_->catalog_hash(), repo_path_, temp_dir, spooler.weak_ref(),
71  download_manager_, params.enforce_limits, params.nested_kcatalog_limit,
73  params.use_autocatalogs, params.max_weight, params.min_weight,
74  cache_dir_);
75  output_catalog_mgr_->Init();
76  }
77 
79 
80  ret &= CreateNewManifest(new_manifest_path);
81  *new_manifest_hash = manifest_->catalog_hash();
82  *final_rev = manifest_->revision();
83 
84  output_catalog_mgr_.Destroy();
85 
86  return ret;
87 }
88 
89 template <typename RwCatalogMgr, typename RoCatalogMgr>
91  const PathString& path) {
92  const PathString rel_path = MakeRelative(path);
93 
94  // Ignore any paths that are not either within the lease path or
95  // above the lease path
96  return !(IsSubPath(lease_path_, rel_path) ||
97  IsSubPath(rel_path, lease_path_));
98 }
99 
100 template <typename RwCatalogMgr, typename RoCatalogMgr>
102  const PathString& path) {
103  const PathString rel_path = MakeRelative(path);
104 
105  // Do not report any changes occurring outside the lease path (which
106  // will be due to other concurrent writers)
107  return IsSubPath(lease_path_, rel_path);
108 }
109 
110 template <typename RwCatalogMgr, typename RoCatalogMgr>
112  const PathString& path, const catalog::DirectoryEntry& entry,
113  const XattrList& xattrs, const FileChunkList& chunks) {
114  const PathString rel_path = MakeRelative(path);
115 
116  const std::string parent_path =
117  std::strchr(rel_path.c_str(), '/') ? GetParentPath(rel_path).c_str() : "";
118 
119  if (entry.IsDirectory()) {
120  if (entry.IsNestedCatalogMountpoint()) {
121  // Install the provided nested catalog in the output catalog manager
122  RoCatalogMgr *new_catalog_mgr =
124  PathString mountpoint;
125  shash::Any nested_hash;
126  uint64_t nested_size;
127  const bool found = new_catalog_mgr->LookupNested(
128  path, &mountpoint, &nested_hash, &nested_size);
129  if (!found || !nested_size) {
131  "CatalogMergeTool - nested catalog %s not found. Aborting",
132  rel_path.c_str());
133  }
134  output_catalog_mgr_->GraftNestedCatalog(rel_path.ToString(),
135  nested_hash, nested_size);
136  return false;
137  } else {
138  output_catalog_mgr_->AddDirectory(entry, xattrs, parent_path);
139  }
140  perf::Inc(counters_->n_directories_added);
141  } else if (entry.IsRegular() || entry.IsLink()) {
142  catalog::DirectoryEntry modified_entry = entry;
143  SplitHardlink(&modified_entry);
144  const catalog::DirectoryEntryBase* base_entry =
145  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
146  if (entry.IsChunkedFile()) {
147  assert(!chunks.IsEmpty());
148  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
149  chunks);
150  } else {
151  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
152  }
153  if (entry.IsLink()) {
154  perf::Inc(counters_->n_symlinks_added);
155  } else {
156  perf::Inc(counters_->n_files_added);
157  }
158  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry.size()));
159  }
160  return true;
161 }
162 
163 template <typename RwCatalogMgr, typename RoCatalogMgr>
165  const PathString& path, const catalog::DirectoryEntry& entry) {
166  const PathString rel_path = MakeRelative(path);
167 
168  if (entry.IsDirectory()) {
169  if (entry.IsNestedCatalogMountpoint()) {
170  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
171  false);
172  }
173 
174  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
175  perf::Inc(counters_->n_directories_removed);
176  } else if (entry.IsRegular() || entry.IsLink()) {
177  AbortIfHardlinked(entry);
178  output_catalog_mgr_->RemoveFile(rel_path.c_str());
179 
180  if (entry.IsLink()) {
181  perf::Inc(counters_->n_symlinks_removed);
182  } else {
183  perf::Inc(counters_->n_files_removed);
184  }
185 
186  perf::Xadd(counters_->sz_removed_bytes, static_cast<int64_t>(entry.size()));
187  }
188 }
189 
190 template <typename RwCatalogMgr, typename RoCatalogMgr>
192  const PathString& path, const catalog::DirectoryEntry& entry1,
193  const catalog::DirectoryEntry& entry2, const XattrList& xattrs,
194  const FileChunkList& chunks) {
195  const PathString rel_path = MakeRelative(path);
196 
197  const std::string parent_path =
198  std::strchr(rel_path.c_str(), '/') ? GetParentPath(rel_path).c_str() : "";
199 
200  if (entry1.IsNestedCatalogMountpoint() &&
201  entry2.IsNestedCatalogMountpoint()) {
202  // From nested catalog to nested catalog
203  RoCatalogMgr *new_catalog_mgr =
205  PathString mountpoint;
206  shash::Any new_hash;
207  uint64_t new_size;
208  const bool found = new_catalog_mgr->LookupNested(path, &mountpoint,
209  &new_hash, &new_size);
210  if (!found || !new_size) {
212  "CatalogMergeTool - nested catalog %s not found. Aborting",
213  rel_path.c_str());
214  }
215  output_catalog_mgr_->SwapNestedCatalog(rel_path.ToString(), new_hash,
216  new_size);
217  return false; // skip recursion into nested catalog mountpoints
218  } else if (entry1.IsDirectory() && entry2.IsDirectory()) {
219  // From directory to directory
220  const catalog::DirectoryEntryBase* base_entry =
221  static_cast<const catalog::DirectoryEntryBase*>(&entry2);
222  output_catalog_mgr_->TouchDirectory(*base_entry, xattrs, rel_path.c_str());
223  if (!entry1.IsNestedCatalogMountpoint() &&
224  entry2.IsNestedCatalogMountpoint()) {
225  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
226  } else if (entry1.IsNestedCatalogMountpoint() &&
227  !entry2.IsNestedCatalogMountpoint()) {
228  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()));
229  }
230  perf::Inc(counters_->n_directories_changed);
231  } else if ((entry1.IsRegular() || entry1.IsLink()) && entry2.IsDirectory()) {
232  // From file to directory
233  AbortIfHardlinked(entry1);
234  output_catalog_mgr_->RemoveFile(rel_path.c_str());
235  output_catalog_mgr_->AddDirectory(entry2, xattrs, parent_path);
236  if (entry2.IsNestedCatalogMountpoint()) {
237  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
238  }
239  if (entry1.IsLink()) {
240  perf::Inc(counters_->n_symlinks_removed);
241  } else {
242  perf::Inc(counters_->n_files_removed);
243  }
244  perf::Xadd(counters_->sz_removed_bytes,
245  static_cast<int64_t>(entry1.size()));
246  perf::Inc(counters_->n_directories_added);
247 
248  } else if (entry1.IsDirectory() && (entry2.IsRegular() || entry2.IsLink())) {
249  // From directory to file
250  if (entry1.IsNestedCatalogMountpoint()) {
251  // we merge the nested catalog with its parent, it will be the recursive
252  // procedure that will take care of deleting all the files.
253  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
254  /* merge = */ true);
255  }
256 
257  catalog::DirectoryEntry modified_entry = entry2;
258  SplitHardlink(&modified_entry);
259  const catalog::DirectoryEntryBase* base_entry =
260  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
261 
262  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
263 
264  if (entry2.IsChunkedFile()) {
265  assert(!chunks.IsEmpty());
266  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
267  chunks);
268  } else {
269  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
270  }
271 
272  perf::Inc(counters_->n_directories_removed);
273  if (entry2.IsLink()) {
274  perf::Inc(counters_->n_symlinks_added);
275  } else {
276  perf::Inc(counters_->n_files_added);
277  }
278  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
279 
280  } else if ((entry1.IsRegular() || entry1.IsLink()) &&
281  (entry2.IsRegular() || entry2.IsLink())) {
282  // From file to file
283  AbortIfHardlinked(entry1);
284  catalog::DirectoryEntry modified_entry = entry2;
285  SplitHardlink(&modified_entry);
286  const catalog::DirectoryEntryBase* base_entry =
287  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
288  output_catalog_mgr_->RemoveFile(rel_path.c_str());
289  if (entry2.IsChunkedFile()) {
290  assert(!chunks.IsEmpty());
291  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
292  chunks);
293  } else {
294  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
295  }
296 
297  if (entry1.IsRegular() && entry2.IsRegular()) {
298  perf::Inc(counters_->n_files_changed);
299  } else if (entry1.IsRegular() && entry2.IsLink()) {
300  perf::Inc(counters_->n_files_removed);
301  perf::Inc(counters_->n_symlinks_added);
302  } else if (entry1.IsLink() && entry2.IsRegular()) {
303  perf::Inc(counters_->n_symlinks_removed);
304  perf::Inc(counters_->n_files_added);
305  } else {
306  perf::Inc(counters_->n_symlinks_changed);
307  }
308  perf::Xadd(counters_->sz_removed_bytes,
309  static_cast<int64_t>(entry1.size()));
310  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
311  }
312  return true;
313 }
314 
315 template <typename RwCatalogMgr, typename RoCatalogMgr>
317  std::string* new_manifest_path) {
318  if (!output_catalog_mgr_->Commit(false, 0, manifest_)) {
320  "CatalogMergeTool - Could not commit output catalog");
321  return false;
322  }
323 
324  const std::string new_path = CreateTempPath(temp_dir_prefix_, 0600);
325 
326  if (!manifest_->Export(new_path)) {
328  "CatalogMergeTool - Could not export new manifest");
329  }
330 
331  *new_manifest_path = new_path;
332 
333  return true;
334 }
335 
336 } // namespace receiver
337 
338 #endif // CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
uint32_t linkcount() const
bool IsSubPath(const PathString &parent, const PathString &path)
virtual bool ReportAddition(const PathString &path, const catalog::DirectoryEntry &entry, const XattrList &xattrs, const FileChunkList &chunks)
bool Export(const std::string &path) const
Definition: manifest.cc:223
int64_t Xadd(class Counter *counter, const int64_t delta)
Definition: statistics.h:51
bool IsDirectory() const
size_t min_weight
Definition: params.h:37
size_t avg_chunk_size
Definition: params.h:28
std::string spooler_configuration
Definition: params.h:20
T * weak_ref() const
Definition: pointer.h:42
bool IsChunkedFile() const
#define PANIC(...)
Definition: exception.h:29
uint64_t size() const
void set_linkcount(const uint32_t linkcount)
zlib::Algorithms compression_alg
Definition: params.h:24
size_t nested_kcatalog_limit
Definition: params.h:31
bool use_autocatalogs
Definition: params.h:35
perf::Statistics * statistics_
Definition: repository.h:139
std::string CreateTempPath(const std::string &path_prefix, const int mode)
Definition: posix.cc:1045
virtual bool ReportModification(const PathString &path, const catalog::DirectoryEntry &old_entry, const catalog::DirectoryEntry &new_entry, const XattrList &xattrs, const FileChunkList &chunks)
assert((mem||(size==0))&&"Out Of Memory")
void AbortIfHardlinked(const catalog::DirectoryEntry &entry)
bool IsNestedCatalogMountpoint() const
uint64_t revision() const
Definition: manifest.h:129
PathString MakeRelative(const PathString &path)
bool use_file_chunking
Definition: params.h:26
bool Run(const Params &params, std::string *new_manifest_path, shash::Any *new_manifest_hash, uint64_t *final_rev)
NameString name() const
bool IsLink() const
size_t max_weight
Definition: params.h:36
size_t max_chunk_size
Definition: params.h:29
bool IsRegular() const
virtual bool IsReportablePath(const PathString &path)
RoCatalogMgr * GetNewCatalogMgr()
virtual bool IsIgnoredPath(const PathString &path)
static RaiiTempDir * Create(const std::string &prefix)
Definition: raii_temp_dir.cc:9
shash::Any catalog_hash() const
Definition: manifest.h:132
void Inc(class Counter *counter)
Definition: statistics.h:50
bool IsEmpty() const
Definition: bigvector.h:72
manifest::Manifest * manifest_
Definition: repository.h:148
std::string ToString() const
Definition: shortstring.h:141
size_t min_chunk_size
Definition: params.h:27
void SplitHardlink(catalog::DirectoryEntry *entry)
size_t file_mbyte_limit
Definition: params.h:33
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:217
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
bool CreateNewManifest(std::string *new_manifest_path)
size_t root_kcatalog_limit
Definition: params.h:32
bool Run(const PathString &path)
bool generate_legacy_bulk_chunks
Definition: params.h:25
const char * c_str() const
Definition: shortstring.h:145
bool enforce_limits
Definition: params.h:30
virtual void ReportRemoval(const PathString &path, const catalog::DirectoryEntry &entry)
shash::Algorithms hash_alg
Definition: params.h:22
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528