GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/sync_mediator.h
Date: 2026-07-12 02:35:48
Exec Total Coverage
Lines: 0 13 0.0%
Branches: 0 12 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * The SyncMediator is an intermediate layer between the UnionSync
5 * implementation and the CatalogManager.
6 * It's main responsibility is to unwind file system intrinsics as
7 * deleting all files in a deleted directory. Furthermore newly
8 * created directories are recursed and all included files are
9 * added as a whole (performance improvement).
10 *
11 * Furthermore it keeps track of hard link relations. As we cannot store the
12 * transient inode of the union file system in cvmfs, we just keep track of
13 * hard link relations itself. Inodes will be assigned at run time of the CVMFS
14 * client taking these relations into account.
15 *
16 * Another responsibility of this class is the creation and destruction
17 * of nested catalogs. If a .cvmfscatalog magic file is encountered,
18 * either on delete or add, it will be treated as nested catalog change.
19 *
20 * New and modified files are piped to external processes for hashing and
21 * compression. Results come back in a pipe.
22 */
23
24 #ifndef CVMFS_SYNC_MEDIATOR_H_
25 #define CVMFS_SYNC_MEDIATOR_H_
26
27 #include <pthread.h>
28
29 #include <map>
30 #include <stack>
31 #include <string>
32 #include <vector>
33
34 #include "catalog_mgr_rw.h"
35 #include "compression/compression.h"
36 #include "file_chunk.h"
37 #include "publish/repository.h"
38 #include "statistics.h"
39 #include "swissknife_sync.h"
40 #include "sync_item.h"
41 #include "util/pointer.h"
42 #include "util/shared_ptr.h"
43 #include "xattr.h"
44
45 namespace manifest {
46 class Manifest;
47 }
48
49 namespace publish {
50
51 class SyncDiffReporter : public DiffListener {
52 public:
53 enum PrintAction {
54 kPrintDots,
55 kPrintChanges
56 };
57
58 explicit SyncDiffReporter(PrintAction print_action = kPrintChanges,
59 unsigned int processing_dot_interval = 100)
60 : print_action_(print_action)
61 , processing_dot_interval_(processing_dot_interval)
62 , changed_items_(0) { }
63
64 virtual void OnInit(const history::History::Tag &from_tag,
65 const history::History::Tag &to_tag);
66 virtual void OnStats(const catalog::DeltaCounters &delta);
67
68 virtual void OnAdd(const std::string &path,
69 const catalog::DirectoryEntry &entry);
70 virtual void OnRemove(const std::string &path,
71 const catalog::DirectoryEntry &entry);
72 virtual void OnModify(const std::string &path,
73 const catalog::DirectoryEntry &entry_from,
74 const catalog::DirectoryEntry &entry_to);
75 void CommitReport();
76
77 private:
78 void PrintDots();
79 void AddImpl(const std::string &path);
80 void RemoveImpl(const std::string &path);
81 void ModifyImpl(const std::string &path);
82
83 PrintAction print_action_;
84 unsigned int processing_dot_interval_;
85 unsigned int changed_items_;
86 };
87
88 /**
89 * If we encounter a file with linkcount > 1 it will be added to a HardlinkGroup
90 * After processing all files, the HardlinkGroups are populated with
91 * related hardlinks
92 * Assertion: linkcount == HardlinkGroup::hardlinks.size() at the end
93 */
94 struct HardlinkGroup {
95 explicit HardlinkGroup(const SharedPtr<SyncItem> &item) : master(item) {
96 hardlinks[master->GetRelativePath()] = item;
97 }
98
99 void AddHardlink(const SharedPtr<SyncItem> &entry) {
100 hardlinks[entry->GetRelativePath()] = entry;
101 }
102
103 SharedPtr<SyncItem> master;
104 SyncItemList hardlinks;
105 FileChunkList file_chunks;
106 };
107
108 class AbstractSyncMediator {
109 public:
110 virtual ~AbstractSyncMediator() = 0;
111
112 virtual void RegisterUnionEngine(SyncUnion *engine) = 0;
113
114 virtual void Add(SharedPtr<SyncItem> entry) = 0;
115 virtual void Touch(SharedPtr<SyncItem> entry) = 0;
116 virtual void Remove(SharedPtr<SyncItem> entry, bool fast_delete = false) = 0;
117 virtual void Replace(SharedPtr<SyncItem> entry) = 0;
118 // Returns false if the source was missing and fail_if_source_missing was
119 // false (the clone was skipped); true otherwise.
120 virtual bool Clone(const std::string from, const std::string to,
121 bool fail_if_source_missing) = 0;
122
123 virtual void AddUnmaterializedDirectory(SharedPtr<SyncItem> entry) = 0;
124
125 virtual void EnterDirectory(SharedPtr<SyncItem> entry) = 0;
126 virtual void LeaveDirectory(SharedPtr<SyncItem> entry) = 0;
127
128 virtual bool Commit(manifest::Manifest *manifest) = 0;
129
130 virtual bool IsExternalData() const = 0;
131 virtual bool IsDirectIo() const = 0;
132 virtual zlib::Algorithms GetCompressionAlgorithm() const = 0;
133 };
134
135 /**
136 * Mapping of inode number to the related HardlinkGroup.
137 */
138 typedef std::map<uint64_t, HardlinkGroup> HardlinkGroupMap;
139
140 /**
141 * List of bundle specification files found in a certain directory
142 * (non-recursive).
143 */
144 typedef std::vector<std::string> BundleSpecs;
145
146 /**
147 * The SyncMediator refines the input received from a concrete UnionSync object.
148 * For example, it resolves the insertion and deletion of complete directories
149 * by recursing them. It works as a mediator between the union file system and
150 * forwards the correct database commands to the catalog handler to sync the
151 * changes into the repository.
152 * Furthermore it sends new and modified files to the spooler for compression
153 * and hashing.
154 */
155 class SyncMediator : public virtual AbstractSyncMediator {
156 public:
157 static const unsigned int processing_dot_interval = 100;
158
159 SyncMediator(catalog::WritableCatalogManager *catalog_manager,
160 const SyncParameters *params,
161 perf::StatisticsTemplate statistics);
162 void RegisterUnionEngine(SyncUnion *engine);
163 // Final class, it is not meant to be derived any further
164 ~SyncMediator();
165
166 void Add(SharedPtr<SyncItem> entry);
167 void Touch(SharedPtr<SyncItem> entry);
168 void Remove(SharedPtr<SyncItem> entry, bool fast_delete = false);
169 void Replace(SharedPtr<SyncItem> entry);
170 bool Clone(const std::string from, const std::string to,
171 bool fail_if_source_missing);
172
173 void AddUnmaterializedDirectory(SharedPtr<SyncItem> entry);
174
175 void EnterDirectory(SharedPtr<SyncItem> entry);
176 void LeaveDirectory(SharedPtr<SyncItem> entry);
177
178 bool Commit(manifest::Manifest *manifest);
179
180 // The sync union engine uses this information to create properly initialized
181 // sync items
182 bool IsExternalData() const { return params_->external_data; }
183 bool IsDirectIo() const { return params_->direct_io; }
184 zlib::Algorithms GetCompressionAlgorithm() const {
185 return params_->compression_alg;
186 }
187
188 private:
189 typedef std::stack<HardlinkGroupMap> HardlinkGroupMapStack;
190 typedef std::vector<HardlinkGroup> HardlinkGroupList;
191
192 void EnsureAllowed(SharedPtr<SyncItem> entry);
193
194 // Called after figuring out the type of a path (file, symlink, dir)
195 void AddFile(SharedPtr<SyncItem> entry);
196 void RemoveFile(SharedPtr<SyncItem> entry);
197
198 void AddDirectory(SharedPtr<SyncItem> entry);
199 void RemoveDirectory(SharedPtr<SyncItem> entry);
200 void TouchDirectory(SharedPtr<SyncItem> entry);
201
202 void CreateNestedCatalog(SharedPtr<SyncItem> directory);
203 void RemoveNestedCatalog(SharedPtr<SyncItem> directory);
204
205 void TouchDirectoryRecursively(SharedPtr<SyncItem> entry);
206 void TouchingFileCallback(const std::string &parent_dir,
207 const std::string &file_name);
208 void TouchingSymlinkCallback(const std::string &parent_dir,
209 const std::string &link_name);
210 void TouchDirectoryCallback(const std::string &parent_dir,
211 const std::string &dir_name);
212 void RemoveDirectoryRecursively(SharedPtr<SyncItem> entry,
213 bool fast_delete = false);
214 void RemoveFileCallback(const std::string &parent_dir,
215 const std::string &file_name);
216 void RemoveSymlinkCallback(const std::string &parent_dir,
217 const std::string &link_name);
218 void RemoveCharacterDeviceCallback(const std::string &parent_dir,
219 const std::string &link_name);
220 void RemoveBlockDeviceCallback(const std::string &parent_dir,
221 const std::string &link_name);
222 void RemoveFifoCallback(const std::string &parent_dir,
223 const std::string &link_name);
224 void RemoveSocketCallback(const std::string &parent_dir,
225 const std::string &link_name);
226 void RemoveDirectoryCallback(const std::string &parent_dir,
227 const std::string &dir_name);
228 bool IgnoreFileCallback(const std::string &parent_dir,
229 const std::string &file_name);
230 // Called by file system traversal
231 void EnterAddedDirectoryCallback(const std::string &parent_dir,
232 const std::string &dir_name);
233 void LeaveAddedDirectoryCallback(const std::string &parent_dir,
234 const std::string &dir_name);
235 void AddDirectoryRecursively(SharedPtr<SyncItem> entry);
236 bool AddDirectoryCallback(const std::string &parent_dir,
237 const std::string &dir_name);
238 void AddFileCallback(const std::string &parent_dir,
239 const std::string &file_name);
240 void AddCharacterDeviceCallback(const std::string &parent_dir,
241 const std::string &file_name);
242 void AddBlockDeviceCallback(const std::string &parent_dir,
243 const std::string &file_name);
244 void AddFifoCallback(const std::string &parent_dir,
245 const std::string &file_name);
246 void AddSocketCallback(const std::string &parent_dir,
247 const std::string &file_name);
248 void AddSymlinkCallback(const std::string &parent_dir,
249 const std::string &link_name);
250 SharedPtr<SyncItem> CreateSyncItem(const std::string &relative_parent_path,
251 const std::string &filename,
252 const SyncItemType entry_type) const;
253
254 // Called by Upload Spooler
255 void PublishFilesCallback(const upload::SpoolerResult &result);
256 void PublishHardlinksCallback(const upload::SpoolerResult &result);
257
258 std::string GetBundleTriggerPath(SharedPtr<SyncItem> bundle_spec_entry) const;
259 void InsertBundleSpec(SharedPtr<SyncItem> entry);
260 void AddBundleSpecs();
261
262 // Hardlink handling
263 void CompleteHardlinks(SharedPtr<SyncItem> entry);
264 HardlinkGroupMap &GetHardlinkMap() { return hardlink_stack_.top(); }
265 void LegacyRegularHardlinkCallback(const std::string &parent_dir,
266 const std::string &file_name);
267 void LegacySymlinkHardlinkCallback(const std::string &parent_dir,
268 const std::string &file_name);
269 void LegacyCharacterDeviceHardlinkCallback(const std::string &parent_dir,
270 const std::string &file_name);
271 void LegacyBlockDeviceHardlinkCallback(const std::string &parent_dir,
272 const std::string &file_name);
273 void LegacyFifoHardlinkCallback(const std::string &parent_dir,
274 const std::string &file_name);
275 void LegacySocketHardlinkCallback(const std::string &parent_dir,
276 const std::string &file_name);
277 void InsertLegacyHardlink(SharedPtr<SyncItem> entry);
278 uint64_t GetTemporaryHardlinkGroupNumber(SharedPtr<SyncItem> entry) const;
279 void InsertHardlink(SharedPtr<SyncItem> entry);
280
281 void AddLocalHardlinkGroups(const HardlinkGroupMap &hardlinks);
282 void AddHardlinkGroup(const HardlinkGroup &group);
283
284 catalog::WritableCatalogManager *catalog_manager_;
285 SyncUnion *union_engine_;
286
287 bool handle_hardlinks_;
288
289 /**
290 * When fast_delete is requested but the target path is not a nested catalog
291 * transition point, we fall back to filesystem traversal. This flag ensures
292 * that any nested catalog transition points encountered during the traversal
293 * are still fast-deleted instead of being recursively traversed.
294 */
295 bool recursive_fast_delete_;
296
297 /**
298 * Hardlinks are supported as long as they all reside in the same directory.
299 * If a recursion enters a directory, we push an empty HardlinkGroupMap to
300 * keep track of the hardlinks of this directory.
301 * When leaving a directory (i.e. it is completely processed) the stack is
302 * popped and the HardlinkGroupMap is processed.
303 */
304 HardlinkGroupMapStack hardlink_stack_;
305
306 /**
307 * Files that trigger preloading a file bundle have a .cvmfsbundle-<filename>
308 * bundle specification file. We don't know in which order we encounter the
309 * main file and the bundle specification file. So we pick up all the bundle
310 * specifications first and compare them to the present files at the end of
311 * the transaction.
312 */
313 BundleSpecs bundle_specs_;
314
315 /**
316 * New and modified files are sent to an external spooler for hashing and
317 * compression. A spooler callback adds them to the catalogs, once processed.
318 */
319 pthread_mutex_t lock_file_queue_;
320 SyncItemList file_queue_;
321
322 HardlinkGroupList hardlink_queue_;
323
324 const SyncParameters *params_;
325 mutable unsigned int changed_items_;
326
327 /**
328 * By default, files have no extended attributes.
329 */
330 XattrList default_xattrs_;
331 UniquePtr<perf::FsCounters> counters_;
332
333 UniquePtr<SyncDiffReporter> reporter_;
334 }; // class SyncMediator
335
336 } // namespace publish
337
338 #endif // CVMFS_SYNC_MEDIATOR_H_
339