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 <set> |
31 |
|
|
#include <stack> |
32 |
|
|
#include <string> |
33 |
|
|
#include <vector> |
34 |
|
|
|
35 |
|
|
#include "catalog_mgr_rw.h" |
36 |
|
|
#include "compression/compression.h" |
37 |
|
|
#include "file_chunk.h" |
38 |
|
|
#include "publish/repository.h" |
39 |
|
|
#include "statistics.h" |
40 |
|
|
#include "swissknife_sync.h" |
41 |
|
|
#include "sync_item.h" |
42 |
|
|
#include "util/platform.h" |
43 |
|
|
#include "util/pointer.h" |
44 |
|
|
#include "util/shared_ptr.h" |
45 |
|
|
#include "xattr.h" |
46 |
|
|
|
47 |
|
|
namespace manifest { |
48 |
|
|
class Manifest; |
49 |
|
|
} |
50 |
|
|
|
51 |
|
|
namespace publish { |
52 |
|
|
|
53 |
|
|
class SyncDiffReporter : public DiffListener { |
54 |
|
|
public: |
55 |
|
|
enum PrintAction { |
56 |
|
|
kPrintDots, |
57 |
|
|
kPrintChanges |
58 |
|
|
}; |
59 |
|
|
|
60 |
|
✗ |
explicit SyncDiffReporter(PrintAction print_action = kPrintChanges, |
61 |
|
|
unsigned int processing_dot_interval = 100) |
62 |
|
✗ |
: print_action_(print_action), |
63 |
|
✗ |
processing_dot_interval_(processing_dot_interval), |
64 |
|
✗ |
changed_items_(0) {} |
65 |
|
|
|
66 |
|
|
virtual void OnInit(const history::History::Tag &from_tag, |
67 |
|
|
const history::History::Tag &to_tag); |
68 |
|
|
virtual void OnStats(const catalog::DeltaCounters &delta); |
69 |
|
|
|
70 |
|
|
virtual void OnAdd(const std::string &path, |
71 |
|
|
const catalog::DirectoryEntry &entry); |
72 |
|
|
virtual void OnRemove(const std::string &path, |
73 |
|
|
const catalog::DirectoryEntry &entry); |
74 |
|
|
virtual void OnModify(const std::string &path, |
75 |
|
|
const catalog::DirectoryEntry &entry_from, |
76 |
|
|
const catalog::DirectoryEntry &entry_to); |
77 |
|
|
void CommitReport(); |
78 |
|
|
|
79 |
|
|
private: |
80 |
|
|
void PrintDots(); |
81 |
|
|
void AddImpl(const std::string &path); |
82 |
|
|
void RemoveImpl(const std::string &path); |
83 |
|
|
void ModifyImpl(const std::string &path); |
84 |
|
|
|
85 |
|
|
PrintAction print_action_; |
86 |
|
|
unsigned int processing_dot_interval_; |
87 |
|
|
unsigned int changed_items_; |
88 |
|
|
}; |
89 |
|
|
|
90 |
|
|
/** |
91 |
|
|
* If we encounter a file with linkcount > 1 it will be added to a HardlinkGroup |
92 |
|
|
* After processing all files, the HardlinkGroups are populated with |
93 |
|
|
* related hardlinks |
94 |
|
|
* Assertion: linkcount == HardlinkGroup::hardlinks.size() at the end |
95 |
|
|
*/ |
96 |
|
|
struct HardlinkGroup { |
97 |
|
✗ |
explicit HardlinkGroup(const SharedPtr<SyncItem> &item) : master(item) { |
98 |
|
✗ |
hardlinks[master->GetRelativePath()] = item; |
99 |
|
|
} |
100 |
|
|
|
101 |
|
✗ |
void AddHardlink(const SharedPtr<SyncItem> &entry) { |
102 |
|
✗ |
hardlinks[entry->GetRelativePath()] = entry; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
SharedPtr<SyncItem> master; |
106 |
|
|
SyncItemList hardlinks; |
107 |
|
|
FileChunkList file_chunks; |
108 |
|
|
}; |
109 |
|
|
|
110 |
|
|
class AbstractSyncMediator { |
111 |
|
|
public: |
112 |
|
|
virtual ~AbstractSyncMediator() = 0; |
113 |
|
|
|
114 |
|
|
virtual void RegisterUnionEngine(SyncUnion *engine) = 0; |
115 |
|
|
|
116 |
|
|
virtual void Add(SharedPtr<SyncItem> entry) = 0; |
117 |
|
|
virtual void Touch(SharedPtr<SyncItem> entry) = 0; |
118 |
|
|
virtual void Remove(SharedPtr<SyncItem> entry) = 0; |
119 |
|
|
virtual void Replace(SharedPtr<SyncItem> entry) = 0; |
120 |
|
|
virtual void Clone(const std::string from, const std::string to) = 0; |
121 |
|
|
|
122 |
|
|
virtual void AddUnmaterializedDirectory(SharedPtr<SyncItem> entry) = 0; |
123 |
|
|
|
124 |
|
|
virtual void EnterDirectory(SharedPtr<SyncItem> entry) = 0; |
125 |
|
|
virtual void LeaveDirectory(SharedPtr<SyncItem> entry) = 0; |
126 |
|
|
|
127 |
|
|
virtual bool Commit(manifest::Manifest *manifest) = 0; |
128 |
|
|
|
129 |
|
|
virtual bool IsExternalData() const = 0; |
130 |
|
|
virtual bool IsDirectIo() const = 0; |
131 |
|
|
virtual zlib::Algorithms GetCompressionAlgorithm() const = 0; |
132 |
|
|
}; |
133 |
|
|
|
134 |
|
|
/** |
135 |
|
|
* Mapping of inode number to the related HardlinkGroup. |
136 |
|
|
*/ |
137 |
|
|
typedef std::map<uint64_t, HardlinkGroup> HardlinkGroupMap; |
138 |
|
|
|
139 |
|
|
/** |
140 |
|
|
* The SyncMediator refines the input received from a concrete UnionSync object. |
141 |
|
|
* For example, it resolves the insertion and deletion of complete directories |
142 |
|
|
* by recursing them. It works as a mediator between the union file system and |
143 |
|
|
* forwards the correct database commands to the catalog handler to sync the |
144 |
|
|
* changes into the repository. |
145 |
|
|
* Furthermore it sends new and modified files to the spooler for compression |
146 |
|
|
* and hashing. |
147 |
|
|
*/ |
148 |
|
|
class SyncMediator : public virtual AbstractSyncMediator { |
149 |
|
|
public: |
150 |
|
|
static const unsigned int processing_dot_interval = 100; |
151 |
|
|
|
152 |
|
|
SyncMediator(catalog::WritableCatalogManager *catalog_manager, |
153 |
|
|
const SyncParameters *params, |
154 |
|
|
perf::StatisticsTemplate statistics); |
155 |
|
|
void RegisterUnionEngine(SyncUnion *engine); |
156 |
|
|
// Final class, it is not meant to be derived any further |
157 |
|
|
~SyncMediator(); |
158 |
|
|
|
159 |
|
|
void Add(SharedPtr<SyncItem> entry); |
160 |
|
|
void Touch(SharedPtr<SyncItem> entry); |
161 |
|
|
void Remove(SharedPtr<SyncItem> entry); |
162 |
|
|
void Replace(SharedPtr<SyncItem> entry); |
163 |
|
|
void Clone(const std::string from, const std::string to); |
164 |
|
|
|
165 |
|
|
void AddUnmaterializedDirectory(SharedPtr<SyncItem> entry); |
166 |
|
|
|
167 |
|
|
void EnterDirectory(SharedPtr<SyncItem> entry); |
168 |
|
|
void LeaveDirectory(SharedPtr<SyncItem> entry); |
169 |
|
|
|
170 |
|
|
bool Commit(manifest::Manifest *manifest); |
171 |
|
|
|
172 |
|
|
// The sync union engine uses this information to create properly initialized |
173 |
|
|
// sync items |
174 |
|
✗ |
bool IsExternalData() const { return params_->external_data; } |
175 |
|
✗ |
bool IsDirectIo() const { return params_->direct_io; } |
176 |
|
✗ |
zlib::Algorithms GetCompressionAlgorithm() const { |
177 |
|
✗ |
return params_->compression_alg; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
private: |
181 |
|
|
typedef std::stack<HardlinkGroupMap> HardlinkGroupMapStack; |
182 |
|
|
typedef std::vector<HardlinkGroup> HardlinkGroupList; |
183 |
|
|
|
184 |
|
|
void EnsureAllowed(SharedPtr<SyncItem> entry); |
185 |
|
|
|
186 |
|
|
// Called after figuring out the type of a path (file, symlink, dir) |
187 |
|
|
void AddFile(SharedPtr<SyncItem> entry); |
188 |
|
|
void RemoveFile(SharedPtr<SyncItem> entry); |
189 |
|
|
|
190 |
|
|
void AddDirectory(SharedPtr<SyncItem> entry); |
191 |
|
|
void RemoveDirectory(SharedPtr<SyncItem> entry); |
192 |
|
|
void TouchDirectory(SharedPtr<SyncItem> entry); |
193 |
|
|
|
194 |
|
|
void CreateNestedCatalog(SharedPtr<SyncItem> directory); |
195 |
|
|
void RemoveNestedCatalog(SharedPtr<SyncItem> directory); |
196 |
|
|
|
197 |
|
|
void TouchDirectoryRecursively(SharedPtr<SyncItem> entry); |
198 |
|
|
void TouchingFileCallback(const std::string &parent_dir, |
199 |
|
|
const std::string &file_name); |
200 |
|
|
void TouchingSymlinkCallback(const std::string &parent_dir, |
201 |
|
|
const std::string &link_name); |
202 |
|
|
void TouchDirectoryCallback(const std::string &parent_dir, |
203 |
|
|
const std::string &dir_name); |
204 |
|
|
void RemoveDirectoryRecursively(SharedPtr<SyncItem> entry); |
205 |
|
|
void RemoveFileCallback(const std::string &parent_dir, |
206 |
|
|
const std::string &file_name); |
207 |
|
|
void RemoveSymlinkCallback(const std::string &parent_dir, |
208 |
|
|
const std::string &link_name); |
209 |
|
|
void RemoveCharacterDeviceCallback(const std::string &parent_dir, |
210 |
|
|
const std::string &link_name); |
211 |
|
|
void RemoveBlockDeviceCallback(const std::string &parent_dir, |
212 |
|
|
const std::string &link_name); |
213 |
|
|
void RemoveFifoCallback(const std::string &parent_dir, |
214 |
|
|
const std::string &link_name); |
215 |
|
|
void RemoveSocketCallback(const std::string &parent_dir, |
216 |
|
|
const std::string &link_name); |
217 |
|
|
void RemoveDirectoryCallback(const std::string &parent_dir, |
218 |
|
|
const std::string &dir_name); |
219 |
|
|
bool IgnoreFileCallback(const std::string &parent_dir, |
220 |
|
|
const std::string &file_name); |
221 |
|
|
// Called by file system traversal |
222 |
|
|
void EnterAddedDirectoryCallback(const std::string &parent_dir, |
223 |
|
|
const std::string &dir_name); |
224 |
|
|
void LeaveAddedDirectoryCallback(const std::string &parent_dir, |
225 |
|
|
const std::string &dir_name); |
226 |
|
|
void AddDirectoryRecursively(SharedPtr<SyncItem> entry); |
227 |
|
|
bool AddDirectoryCallback(const std::string &parent_dir, |
228 |
|
|
const std::string &dir_name); |
229 |
|
|
void AddFileCallback(const std::string &parent_dir, |
230 |
|
|
const std::string &file_name); |
231 |
|
|
void AddCharacterDeviceCallback(const std::string &parent_dir, |
232 |
|
|
const std::string &file_name); |
233 |
|
|
void AddBlockDeviceCallback(const std::string &parent_dir, |
234 |
|
|
const std::string &file_name); |
235 |
|
|
void AddFifoCallback(const std::string &parent_dir, |
236 |
|
|
const std::string &file_name); |
237 |
|
|
void AddSocketCallback(const std::string &parent_dir, |
238 |
|
|
const std::string &file_name); |
239 |
|
|
void AddSymlinkCallback(const std::string &parent_dir, |
240 |
|
|
const std::string &link_name); |
241 |
|
|
SharedPtr<SyncItem> CreateSyncItem(const std::string &relative_parent_path, |
242 |
|
|
const std::string &filename, |
243 |
|
|
const SyncItemType entry_type) const; |
244 |
|
|
|
245 |
|
|
// Called by Upload Spooler |
246 |
|
|
void PublishFilesCallback(const upload::SpoolerResult &result); |
247 |
|
|
void PublishHardlinksCallback(const upload::SpoolerResult &result); |
248 |
|
|
|
249 |
|
|
// Hardlink handling |
250 |
|
|
void CompleteHardlinks(SharedPtr<SyncItem> entry); |
251 |
|
✗ |
HardlinkGroupMap &GetHardlinkMap() { return hardlink_stack_.top(); } |
252 |
|
|
void LegacyRegularHardlinkCallback(const std::string &parent_dir, |
253 |
|
|
const std::string &file_name); |
254 |
|
|
void LegacySymlinkHardlinkCallback(const std::string &parent_dir, |
255 |
|
|
const std::string &file_name); |
256 |
|
|
void LegacyCharacterDeviceHardlinkCallback(const std::string &parent_dir, |
257 |
|
|
const std::string &file_name); |
258 |
|
|
void LegacyBlockDeviceHardlinkCallback(const std::string &parent_dir, |
259 |
|
|
const std::string &file_name); |
260 |
|
|
void LegacyFifoHardlinkCallback(const std::string &parent_dir, |
261 |
|
|
const std::string &file_name); |
262 |
|
|
void LegacySocketHardlinkCallback(const std::string &parent_dir, |
263 |
|
|
const std::string &file_name); |
264 |
|
|
void InsertLegacyHardlink(SharedPtr<SyncItem> entry); |
265 |
|
|
uint64_t GetTemporaryHardlinkGroupNumber(SharedPtr<SyncItem> entry) const; |
266 |
|
|
void InsertHardlink(SharedPtr<SyncItem> entry); |
267 |
|
|
|
268 |
|
|
void AddLocalHardlinkGroups(const HardlinkGroupMap &hardlinks); |
269 |
|
|
void AddHardlinkGroup(const HardlinkGroup &group); |
270 |
|
|
|
271 |
|
|
catalog::WritableCatalogManager *catalog_manager_; |
272 |
|
|
SyncUnion *union_engine_; |
273 |
|
|
|
274 |
|
|
bool handle_hardlinks_; |
275 |
|
|
|
276 |
|
|
/** |
277 |
|
|
* Hardlinks are supported as long as they all reside in the same directory. |
278 |
|
|
* If a recursion enters a directory, we push an empty HardlinkGroupMap to |
279 |
|
|
* keep track of the hardlinks of this directory. |
280 |
|
|
* When leaving a directory (i.e. it is completely processed) the stack is |
281 |
|
|
* popped and the HardlinkGroupMap is processed. |
282 |
|
|
*/ |
283 |
|
|
HardlinkGroupMapStack hardlink_stack_; |
284 |
|
|
|
285 |
|
|
/** |
286 |
|
|
* New and modified files are sent to an external spooler for hashing and |
287 |
|
|
* compression. A spooler callback adds them to the catalogs, once processed. |
288 |
|
|
*/ |
289 |
|
|
pthread_mutex_t lock_file_queue_; |
290 |
|
|
SyncItemList file_queue_; |
291 |
|
|
|
292 |
|
|
HardlinkGroupList hardlink_queue_; |
293 |
|
|
|
294 |
|
|
const SyncParameters *params_; |
295 |
|
|
mutable unsigned int changed_items_; |
296 |
|
|
|
297 |
|
|
/** |
298 |
|
|
* By default, files have no extended attributes. |
299 |
|
|
*/ |
300 |
|
|
XattrList default_xattrs_; |
301 |
|
|
UniquePtr<perf::FsCounters> counters_; |
302 |
|
|
|
303 |
|
|
UniquePtr<SyncDiffReporter> reporter_; |
304 |
|
|
}; // class SyncMediator |
305 |
|
|
|
306 |
|
|
} // namespace publish |
307 |
|
|
|
308 |
|
|
#endif // CVMFS_SYNC_MEDIATOR_H_ |
309 |
|
|
|