CernVM-FS  2.12.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, 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 
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  output_catalog_mgr_->AddDirectory(entry, xattrs, parent_path);
121  if (entry.IsNestedCatalogMountpoint()) {
122  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
123  }
124  perf::Inc(counters_->n_directories_added);
125  } else if (entry.IsRegular() || entry.IsLink()) {
126  catalog::DirectoryEntry modified_entry = entry;
127  SplitHardlink(&modified_entry);
128  const catalog::DirectoryEntryBase* base_entry =
129  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
130  if (entry.IsChunkedFile()) {
131  assert(!chunks.IsEmpty());
132  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
133  chunks);
134  } else {
135  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
136  }
137  if (entry.IsLink()) {
138  perf::Inc(counters_->n_symlinks_added);
139  } else {
140  perf::Inc(counters_->n_files_added);
141  }
142  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry.size()));
143  }
144 }
145 
146 template <typename RwCatalogMgr, typename RoCatalogMgr>
148  const PathString& path, const catalog::DirectoryEntry& entry) {
149  const PathString rel_path = MakeRelative(path);
150 
151  if (entry.IsDirectory()) {
152  if (entry.IsNestedCatalogMountpoint()) {
153  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
154  false);
155  }
156 
157  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
158  perf::Inc(counters_->n_directories_removed);
159  } else if (entry.IsRegular() || entry.IsLink()) {
160  AbortIfHardlinked(entry);
161  output_catalog_mgr_->RemoveFile(rel_path.c_str());
162 
163  if (entry.IsLink()) {
164  perf::Inc(counters_->n_symlinks_removed);
165  } else {
166  perf::Inc(counters_->n_files_removed);
167  }
168 
169  perf::Xadd(counters_->sz_removed_bytes, static_cast<int64_t>(entry.size()));
170  }
171 }
172 
173 template <typename RwCatalogMgr, typename RoCatalogMgr>
175  const PathString& path, const catalog::DirectoryEntry& entry1,
176  const catalog::DirectoryEntry& entry2, const XattrList& xattrs,
177  const FileChunkList& chunks) {
178  const PathString rel_path = MakeRelative(path);
179 
180  const std::string parent_path =
181  std::strchr(rel_path.c_str(), '/') ? GetParentPath(rel_path).c_str() : "";
182 
183  if (entry1.IsNestedCatalogMountpoint() &&
184  entry2.IsNestedCatalogMountpoint()) {
185  // From nested catalog to nested catalog
186  RoCatalogMgr *new_catalog_mgr =
188  PathString mountpoint;
189  shash::Any new_hash;
190  uint64_t new_size;
191  const bool found = new_catalog_mgr->LookupNested(path, &mountpoint,
192  &new_hash, &new_size);
193  if (!found || !new_size) {
195  "CatalogMergeTool - nested catalog %s not found. Aborting",
196  rel_path.c_str());
197  }
198  output_catalog_mgr_->SwapNestedCatalog(rel_path.ToString(), new_hash,
199  new_size);
200  return false; // skip recursion into nested catalog mountpoints
201  } else if (entry1.IsDirectory() && entry2.IsDirectory()) {
202  // From directory to directory
203  const catalog::DirectoryEntryBase* base_entry =
204  static_cast<const catalog::DirectoryEntryBase*>(&entry2);
205  output_catalog_mgr_->TouchDirectory(*base_entry, xattrs, rel_path.c_str());
206  if (!entry1.IsNestedCatalogMountpoint() &&
207  entry2.IsNestedCatalogMountpoint()) {
208  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
209  } else if (entry1.IsNestedCatalogMountpoint() &&
210  !entry2.IsNestedCatalogMountpoint()) {
211  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()));
212  }
213  perf::Inc(counters_->n_directories_changed);
214  } else if ((entry1.IsRegular() || entry1.IsLink()) && entry2.IsDirectory()) {
215  // From file to directory
216  AbortIfHardlinked(entry1);
217  output_catalog_mgr_->RemoveFile(rel_path.c_str());
218  output_catalog_mgr_->AddDirectory(entry2, xattrs, parent_path);
219  if (entry2.IsNestedCatalogMountpoint()) {
220  output_catalog_mgr_->CreateNestedCatalog(std::string(rel_path.c_str()));
221  }
222  if (entry1.IsLink()) {
223  perf::Inc(counters_->n_symlinks_removed);
224  } else {
225  perf::Inc(counters_->n_files_removed);
226  }
227  perf::Xadd(counters_->sz_removed_bytes,
228  static_cast<int64_t>(entry1.size()));
229  perf::Inc(counters_->n_directories_added);
230 
231  } else if (entry1.IsDirectory() && (entry2.IsRegular() || entry2.IsLink())) {
232  // From directory to file
233  if (entry1.IsNestedCatalogMountpoint()) {
234  // we merge the nested catalog with its parent, it will be the recursive
235  // procedure that will take care of deleting all the files.
236  output_catalog_mgr_->RemoveNestedCatalog(std::string(rel_path.c_str()),
237  /* merge = */ true);
238  }
239 
240  catalog::DirectoryEntry modified_entry = entry2;
241  SplitHardlink(&modified_entry);
242  const catalog::DirectoryEntryBase* base_entry =
243  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
244 
245  output_catalog_mgr_->RemoveDirectory(rel_path.c_str());
246 
247  if (entry2.IsChunkedFile()) {
248  assert(!chunks.IsEmpty());
249  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
250  chunks);
251  } else {
252  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
253  }
254 
255  perf::Inc(counters_->n_directories_removed);
256  if (entry2.IsLink()) {
257  perf::Inc(counters_->n_symlinks_added);
258  } else {
259  perf::Inc(counters_->n_files_added);
260  }
261  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
262 
263  } else if ((entry1.IsRegular() || entry1.IsLink()) &&
264  (entry2.IsRegular() || entry2.IsLink())) {
265  // From file to file
266  AbortIfHardlinked(entry1);
267  catalog::DirectoryEntry modified_entry = entry2;
268  SplitHardlink(&modified_entry);
269  const catalog::DirectoryEntryBase* base_entry =
270  static_cast<const catalog::DirectoryEntryBase*>(&modified_entry);
271  output_catalog_mgr_->RemoveFile(rel_path.c_str());
272  if (entry2.IsChunkedFile()) {
273  assert(!chunks.IsEmpty());
274  output_catalog_mgr_->AddChunkedFile(*base_entry, xattrs, parent_path,
275  chunks);
276  } else {
277  output_catalog_mgr_->AddFile(*base_entry, xattrs, parent_path);
278  }
279 
280  if (entry1.IsRegular() && entry2.IsRegular()) {
281  perf::Inc(counters_->n_files_changed);
282  } else if (entry1.IsRegular() && entry2.IsLink()) {
283  perf::Inc(counters_->n_files_removed);
284  perf::Inc(counters_->n_symlinks_added);
285  } else if (entry1.IsLink() && entry2.IsRegular()) {
286  perf::Inc(counters_->n_symlinks_removed);
287  perf::Inc(counters_->n_files_added);
288  } else {
289  perf::Inc(counters_->n_symlinks_changed);
290  }
291  perf::Xadd(counters_->sz_removed_bytes,
292  static_cast<int64_t>(entry1.size()));
293  perf::Xadd(counters_->sz_added_bytes, static_cast<int64_t>(entry2.size()));
294  }
295  return true;
296 }
297 
298 template <typename RwCatalogMgr, typename RoCatalogMgr>
300  std::string* new_manifest_path) {
301  if (!output_catalog_mgr_->Commit(false, 0, manifest_)) {
303  "CatalogMergeTool - Could not commit output catalog");
304  return false;
305  }
306 
307  const std::string new_path = CreateTempPath(temp_dir_prefix_, 0600);
308 
309  if (!manifest_->Export(new_path)) {
311  "CatalogMergeTool - Could not export new manifest");
312  }
313 
314  *new_manifest_path = new_path;
315 
316  return true;
317 }
318 
319 } // namespace receiver
320 
321 #endif // CVMFS_RECEIVER_CATALOG_MERGE_TOOL_IMPL_H_
uint32_t linkcount() const
bool IsSubPath(const PathString &parent, const PathString &path)
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:1034
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
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()
bool Run(const Params &params, std::string *new_manifest_path, uint64_t *final_rev)
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)
virtual void ReportAddition(const PathString &path, const catalog::DirectoryEntry &entry, const XattrList &xattrs, const FileChunkList &chunks)
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