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.",
38  entry->name().c_str());
39  entry->set_linkcount(1);
40  }
41 }
42 
43 inline void AbortIfHardlinked(const catalog::DirectoryEntry &entry) {
44  if (entry.linkcount() > 1) {
46  "CatalogMergeTool - Removal of file %s with linkcount > 1 is "
47  "not supported. Aborting",
48  entry.name().c_str());
49  }
50 }
51 
52 namespace receiver {
53 
54 template<typename RwCatalogMgr, typename RoCatalogMgr>
56  const Params &params, std::string *new_manifest_path,
57  shash::Any *new_manifest_hash, uint64_t *final_rev) {
59  perf::StatisticsTemplate stats_tmpl("publish", statistics_);
60  counters_ = new perf::FsCounters(stats_tmpl);
61 
62  UniquePtr<RaiiTempDir> raii_temp_dir(RaiiTempDir::Create(temp_dir_prefix_));
63  if (needs_setup_) {
64  upload::SpoolerDefinition definition(
65  params.spooler_configuration, params.hash_alg, params.compression_alg,
67  params.min_chunk_size, params.avg_chunk_size, params.max_chunk_size,
68  "dummy_token", "dummy_key");
69  spooler = upload::Spooler::Construct(definition, &stats_tmpl);
70  const std::string temp_dir = raii_temp_dir->dir();
71  output_catalog_mgr_ = new RwCatalogMgr(
72  manifest_->catalog_hash(), repo_path_, temp_dir, spooler.weak_ref(),
73  download_manager_, params.enforce_limits, params.nested_kcatalog_limit,
75  params.use_autocatalogs, params.max_weight, params.min_weight,
76  cache_dir_);
77  output_catalog_mgr_->Init();
78  }
79 
81 
82  ret &= CreateNewManifest(new_manifest_path);
83  *new_manifest_hash = manifest_->catalog_hash();
84  *final_rev = manifest_->revision();
85 
86  output_catalog_mgr_.Destroy();
87 
88  return ret;
89 }
90 
91 template<typename RwCatalogMgr, typename RoCatalogMgr>
93  const PathString &path) {
94  const PathString rel_path = MakeRelative(path);
95 
96  // Ignore any paths that are not either within the lease path or
97  // above the lease path
98  return !(IsSubPath(lease_path_, rel_path)
99  || IsSubPath(rel_path, lease_path_));
100 }
101 
102 template<typename RwCatalogMgr, typename RoCatalogMgr>
104  const PathString &path) {
105  const PathString rel_path = MakeRelative(path);
106 
107  // Do not report any changes occurring outside the lease path (which
108  // will be due to other concurrent writers)
109  return IsSubPath(lease_path_, rel_path);
110 }
111 
112 template<typename RwCatalogMgr, typename RoCatalogMgr>
114  const PathString &path, const catalog::DirectoryEntry &entry,
115  const XattrList &xattrs, const FileChunkList &chunks) {
116  const PathString rel_path = MakeRelative(path);
117 
118  const std::string parent_path = std::strchr(rel_path.c_str(), '/')
119  ? GetParentPath(rel_path).c_str()
120  : "";
121 
122  if (entry.IsDirectory()) {
123  if (entry.IsNestedCatalogMountpoint()) {
124  // Install the provided nested catalog in the output catalog manager
125  RoCatalogMgr
127  PathString mountpoint;
128  shash::Any nested_hash;
129  uint64_t nested_size;
130  const bool found = new_catalog_mgr->LookupNested(
131  path, &mountpoint, &nested_hash, &nested_size);
132  if (!found || !nested_size) {
134  "CatalogMergeTool - nested catalog %s not found. Aborting",
135  rel_path.c_str());
136  }
137  output_catalog_mgr_->GraftNestedCatalog(rel_path.ToString(), nested_hash,
138  nested_size);
139  return false;
140  } else {
141  output_catalog_mgr_->AddDirectory(entry, xattrs, parent_path);
142  }
143  perf::Inc(counters_->n_directories_added);
144  } else if (entry.IsRegular() || entry.IsLink()) {
145  catalog::DirectoryEntry modified_entry = entry;
146  SplitHardlink(&modified_entry);
148  *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
149  &modified_entry);
150  if (entry.IsChunkedFile()) {
151  assert(!chunks.IsEmpty());
152  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
153  chunks);
154  } else {
155  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
156  }
157  if (entry.IsLink()) {
158  perf::Inc(counters_->n_symlinks_added);
159  } else {
160  perf::Inc(counters_->n_files_added);
161  }
162  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry.size()));
163  }
164  return true;
165 }
166 
167 template<typename RwCatalogMgr, typename RoCatalogMgr>
169  const PathString &path, const catalog::DirectoryEntry &entry) {
170  const PathString rel_path = MakeRelative(path);
171 
172  if (entry.IsDirectory()) {
173  if (entry.IsNestedCatalogMountpoint()) {
174  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
175  false);
176  }
177 
178  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
179  perf::Inc(counters_->n_directories_removed);
180  } else if (entry.IsRegular() || entry.IsLink()) {
181  AbortIfHardlinked(entry);
182  output_catalog_mgr_->RemoveFile(rel_path.c_str());
183 
184  if (entry.IsLink()) {
185  perf::Inc(counters_->n_symlinks_removed);
186  } else {
187  perf::Inc(counters_->n_files_removed);
188  }
189 
190  perf::Xadd(counters_->sz_removed_bytes, static_cast<int64_t>(entry.size()));
191  }
192 }
193 
194 template<typename RwCatalogMgr, typename RoCatalogMgr>
196  const PathString &path, const catalog::DirectoryEntry &entry1,
197  const catalog::DirectoryEntry &entry2, const XattrList &xattrs,
198  const FileChunkList &chunks) {
199  const PathString rel_path = MakeRelative(path);
200 
201  const std::string parent_path = std::strchr(rel_path.c_str(), '/')
202  ? GetParentPath(rel_path).c_str()
203  : "";
204 
205  if (entry1.IsNestedCatalogMountpoint()
206  && entry2.IsNestedCatalogMountpoint()) {
207  // From nested catalog to nested catalog
208  RoCatalogMgr
210  PathString mountpoint;
211  shash::Any new_hash;
212  uint64_t new_size;
213  const bool found = new_catalog_mgr->LookupNested(path, &mountpoint,
214  &new_hash, &new_size);
215  if (!found || !new_size) {
217  "CatalogMergeTool - nested catalog %s not found. Aborting",
218  rel_path.c_str());
219  }
220  output_catalog_mgr_->SwapNestedCatalog(rel_path.ToString(), new_hash,
221  new_size);
222  return false; // skip recursion into nested catalog mountpoints
223  } else if (entry1.IsDirectory() && entry2.IsDirectory()) {
224  // From directory to directory
226  *base_entry = static_cast<const catalog::DirectoryEntryBase *>(&entry2);
227  output_catalog_mgr_->TouchDirectory(*base_entry, xattrs, rel_path.c_str());
228  if (!entry1.IsNestedCatalogMountpoint()
229  && entry2.IsNestedCatalogMountpoint()) {
230  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
231  } else if (entry1.IsNestedCatalogMountpoint()
232  && !entry2.IsNestedCatalogMountpoint()) {
233  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()));
234  }
235  perf::Inc(counters_->n_directories_changed);
236  } else if ((entry1.IsRegular() || entry1.IsLink()) && entry2.IsDirectory()) {
237  // From file to directory
238  AbortIfHardlinked(entry1);
239  output_catalog_mgr_->RemoveFile(rel_path.c_str());
240  output_catalog_mgr_->AddDirectory(entry2, xattrs, parent_path);
241  if (entry2.IsNestedCatalogMountpoint()) {
242  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
243  }
244  if (entry1.IsLink()) {
245  perf::Inc(counters_->n_symlinks_removed);
246  } else {
247  perf::Inc(counters_->n_files_removed);
248  }
249  perf::Xadd(counters_->sz_removed_bytes,
250  static_cast<int64_t>(entry1.size()));
251  perf::Inc(counters_->n_directories_added);
252 
253  } else if (entry1.IsDirectory() && (entry2.IsRegular() || entry2.IsLink())) {
254  // From directory to file
255  if (entry1.IsNestedCatalogMountpoint()) {
256  // we merge the nested catalog with its parent, it will be the recursive
257  // procedure that will take care of deleting all the files.
258  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
259  /* merge = */ true);
260  }
261 
262  catalog::DirectoryEntry modified_entry = entry2;
263  SplitHardlink(&modified_entry);
265  *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
266  &modified_entry);
267 
268  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
269 
270  if (entry2.IsChunkedFile()) {
271  assert(!chunks.IsEmpty());
272  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
273  chunks);
274  } else {
275  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
276  }
277 
278  perf::Inc(counters_->n_directories_removed);
279  if (entry2.IsLink()) {
280  perf::Inc(counters_->n_symlinks_added);
281  } else {
282  perf::Inc(counters_->n_files_added);
283  }
284  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
285 
286  } else if ((entry1.IsRegular() || entry1.IsLink())
287  && (entry2.IsRegular() || entry2.IsLink())) {
288  // From file to file
289  AbortIfHardlinked(entry1);
290  catalog::DirectoryEntry modified_entry = entry2;
291  SplitHardlink(&modified_entry);
293  *base_entry = static_cast<const catalog::DirectoryEntryBase *>(
294  &modified_entry);
295  output_catalog_mgr_->RemoveFile(rel_path.c_str());
296  if (entry2.IsChunkedFile()) {
297  assert(!chunks.IsEmpty());
298  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
299  chunks);
300  } else {
301  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
302  }
303 
304  if (entry1.IsRegular() && entry2.IsRegular()) {
305  perf::Inc(counters_->n_files_changed);
306  } else if (entry1.IsRegular() && entry2.IsLink()) {
307  perf::Inc(counters_->n_files_removed);
308  perf::Inc(counters_->n_symlinks_added);
309  } else if (entry1.IsLink() && entry2.IsRegular()) {
310  perf::Inc(counters_->n_symlinks_removed);
311  perf::Inc(counters_->n_files_added);
312  } else {
313  perf::Inc(counters_->n_symlinks_changed);
314  }
315  perf::Xadd(counters_->sz_removed_bytes,
316  static_cast<int64_t>(entry1.size()));
317  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
318  }
319  return true;
320 }
321 
322 template<typename RwCatalogMgr, typename RoCatalogMgr>
324  std::string *new_manifest_path) {
325  if (!output_catalog_mgr_->Commit(false, 0, manifest_)) {
327  "CatalogMergeTool - Could not commit output catalog");
328  return false;
329  }
330 
331  const std::string new_path = CreateTempPath(temp_dir_prefix_, 0600);
332 
333  if (!manifest_->Export(new_path)) {
335  "CatalogMergeTool - Could not export new manifest");
336  }
337 
338  *new_manifest_path = new_path;
339 
340  return true;
341 }
342 
343 } // namespace receiver
344 
345 #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:217
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:46
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:138
std::string CreateTempPath(const std::string &path_prefix, const int mode)
Definition: posix.cc:1041
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:122
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:125
void Inc(class Counter *counter)
Definition: statistics.h:50
bool IsEmpty() const
Definition: bigvector.h:70
manifest::Manifest * manifest_
Definition: repository.h:147
std::string ToString() const
Definition: shortstring.h:139
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:213
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:14
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:143
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:545