Directory: | cvmfs/ |
---|---|
File: | cvmfs/catalog_rw.cc |
Date: | 2025-02-09 02:34:19 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 310 | 399 | 77.7% |
Branches: | 265 | 722 | 36.7% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #define __STDC_FORMAT_MACROS | ||
6 | |||
7 | #include "catalog_rw.h" | ||
8 | |||
9 | #include <inttypes.h> | ||
10 | #include <cstdio> | ||
11 | #include <cstdlib> | ||
12 | |||
13 | #include "util/concurrency.h" | ||
14 | #include "util/exception.h" | ||
15 | #include "util/logging.h" | ||
16 | #include "xattr.h" | ||
17 | |||
18 | using namespace std; // NOLINT | ||
19 | |||
20 | namespace catalog { | ||
21 | |||
22 | const double WritableCatalog::kMaximalFreePageRatio = 0.20; | ||
23 | const double WritableCatalog::kMaximalRowIdWasteRatio = 0.25; | ||
24 | |||
25 | |||
26 | 89 | WritableCatalog::WritableCatalog(const string &path, | |
27 | const shash::Any &catalog_hash, | ||
28 | Catalog *parent, | ||
29 | 89 | const bool is_not_root) : | |
30 | 178 | Catalog(PathString(path.data(), path.length()), | |
31 | catalog_hash, // This is 0 for a newly created catalog! | ||
32 | parent, | ||
33 | is_not_root), | ||
34 | 89 | sql_insert_(NULL), | |
35 | 89 | sql_unlink_(NULL), | |
36 | 89 | sql_touch_(NULL), | |
37 | 89 | sql_update_(NULL), | |
38 | 89 | sql_chunk_insert_(NULL), | |
39 | 89 | sql_chunks_remove_(NULL), | |
40 | 89 | sql_chunks_count_(NULL), | |
41 | 89 | sql_max_link_id_(NULL), | |
42 | 89 | sql_inc_linkcount_(NULL), | |
43 |
2/4✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 89 times.
✗ Branch 6 not taken.
|
267 | dirty_(false) |
44 | { | ||
45 | 89 | atomic_init32(&dirty_children_); | |
46 | 89 | } | |
47 | |||
48 | |||
49 | 22 | WritableCatalog *WritableCatalog::AttachFreely(const string &root_path, | |
50 | const string &file, | ||
51 | const shash::Any &catalog_hash, | ||
52 | Catalog *parent, | ||
53 | const bool is_not_root) { | ||
54 | WritableCatalog *catalog = | ||
55 |
1/2✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
|
22 | new WritableCatalog(root_path, catalog_hash, parent, is_not_root); |
56 | 22 | const bool successful_init = catalog->InitStandalone(file); | |
57 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
|
22 | if (!successful_init) { |
58 | ✗ | delete catalog; | |
59 | ✗ | return NULL; | |
60 | } | ||
61 | 22 | return catalog; | |
62 | } | ||
63 | |||
64 | |||
65 | 348 | WritableCatalog::~WritableCatalog() { | |
66 | // CAUTION HOT! | ||
67 | // (see Catalog.h - near the definition of FinalizePreparedStatements) | ||
68 | 174 | FinalizePreparedStatements(); | |
69 | 348 | } | |
70 | |||
71 | |||
72 | 75 | void WritableCatalog::Transaction() { | |
73 |
1/3✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
75 | LogCvmfs(kLogCatalog, kLogVerboseMsg, "opening SQLite transaction for '%s'", |
74 | 150 | mountpoint().c_str()); | |
75 | 75 | const bool retval = database().BeginTransaction(); | |
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | assert(retval == true); |
77 | 75 | } | |
78 | |||
79 | |||
80 | 68 | void WritableCatalog::Commit() { | |
81 |
1/3✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
68 | LogCvmfs(kLogCatalog, kLogVerboseMsg, "closing SQLite transaction for '%s'", |
82 | 136 | mountpoint().c_str()); | |
83 | 68 | const bool retval = database().CommitTransaction(); | |
84 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 68 times.
|
68 | assert(retval == true); |
85 | 68 | dirty_ = false; | |
86 | 68 | } | |
87 | |||
88 | |||
89 | 89 | void WritableCatalog::InitPreparedStatements() { | |
90 | 89 | Catalog::InitPreparedStatements(); // polymorphism: up call | |
91 | |||
92 |
3/6✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 89 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 89 times.
✗ Branch 10 not taken.
|
89 | bool retval = SqlCatalog(database(), "PRAGMA foreign_keys = ON;").Execute(); |
93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 89 times.
|
89 | assert(retval); |
94 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_insert_ = new SqlDirentInsert (database()); |
95 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_unlink_ = new SqlDirentUnlink (database()); |
96 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_touch_ = new SqlDirentTouch (database()); |
97 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_update_ = new SqlDirentUpdate (database()); |
98 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_chunk_insert_ = new SqlChunkInsert (database()); |
99 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_chunks_remove_ = new SqlChunksRemove (database()); |
100 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_chunks_count_ = new SqlChunksCount (database()); |
101 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_max_link_id_ = new SqlMaxHardlinkGroup (database()); |
102 |
1/2✓ Branch 3 taken 89 times.
✗ Branch 4 not taken.
|
89 | sql_inc_linkcount_ = new SqlIncLinkcount (database()); |
103 | 89 | } | |
104 | |||
105 | |||
106 | 87 | void WritableCatalog::FinalizePreparedStatements() { | |
107 | // no polymorphism: no up call (see Catalog.h - | ||
108 | // near the definition of this method) | ||
109 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_insert_; |
110 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_unlink_; |
111 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_touch_; |
112 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_update_; |
113 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_chunk_insert_; |
114 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_chunks_remove_; |
115 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_chunks_count_; |
116 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_max_link_id_; |
117 |
1/2✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
|
87 | delete sql_inc_linkcount_; |
118 | 87 | } | |
119 | |||
120 | |||
121 | /** | ||
122 | * Find out the maximal hardlink group id in this catalog. | ||
123 | */ | ||
124 | 1 | uint32_t WritableCatalog::GetMaxLinkId() const { | |
125 | 1 | int result = -1; | |
126 | |||
127 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | if (sql_max_link_id_->FetchRow()) { |
128 | 1 | result = sql_max_link_id_->GetMaxGroupId(); | |
129 | } | ||
130 | 1 | sql_max_link_id_->Reset(); | |
131 | |||
132 | 1 | return result; | |
133 | } | ||
134 | |||
135 | |||
136 | /** | ||
137 | * Adds a directory entry. | ||
138 | * @param entry the DirectoryEntry to add to the catalog | ||
139 | * @param entry_path the full path of the DirectoryEntry to add | ||
140 | * @param parent_path the full path of the containing directory | ||
141 | */ | ||
142 | 461 | void WritableCatalog::AddEntry( | |
143 | const DirectoryEntry &entry, | ||
144 | const XattrList &xattrs, | ||
145 | const string &entry_path, | ||
146 | const string &parent_path) | ||
147 | { | ||
148 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | SetDirty(); |
149 | |||
150 |
1/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 461 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
461 | LogCvmfs(kLogCatalog, kLogVerboseMsg, "add entry '%s' to '%s'", |
151 | entry_path.c_str(), | ||
152 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
922 | mountpoint().c_str()); |
153 | |||
154 |
1/2✓ Branch 2 taken 461 times.
✗ Branch 3 not taken.
|
461 | shash::Md5 path_hash((shash::AsciiPtr(entry_path))); |
155 |
1/2✓ Branch 2 taken 461 times.
✗ Branch 3 not taken.
|
461 | shash::Md5 parent_hash((shash::AsciiPtr(parent_path))); |
156 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | DirectoryEntry effective_entry(entry); |
157 | 461 | effective_entry.set_has_xattrs(!xattrs.IsEmpty()); | |
158 | |||
159 | bool retval = | ||
160 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | sql_insert_->BindPathHash(path_hash) && |
161 |
3/6✓ Branch 0 taken 461 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 461 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 461 times.
✗ Branch 6 not taken.
|
922 | sql_insert_->BindParentPathHash(parent_hash) && |
162 |
2/4✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 461 times.
✗ Branch 4 not taken.
|
461 | sql_insert_->BindDirent(effective_entry); |
163 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 461 times.
|
461 | assert(retval); |
164 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | if (xattrs.IsEmpty()) { |
165 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | retval = sql_insert_->BindXattrEmpty(); |
166 | } else { | ||
167 | ✗ | retval = sql_insert_->BindXattr(xattrs); | |
168 | } | ||
169 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 461 times.
|
461 | assert(retval); |
170 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | retval = sql_insert_->Execute(); |
171 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 461 times.
|
461 | assert(retval); |
172 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | sql_insert_->Reset(); |
173 | |||
174 |
1/2✓ Branch 1 taken 461 times.
✗ Branch 2 not taken.
|
461 | delta_counters_.Increment(effective_entry); |
175 | 461 | } | |
176 | |||
177 | |||
178 | /** | ||
179 | * Removes the specified entry from the catalog. | ||
180 | * Note: removing a directory which is non-empty results in dangling entries. | ||
181 | * (this should be treated in upper layers) | ||
182 | * @param entry_path the full path of the DirectoryEntry to delete | ||
183 | */ | ||
184 | 113 | void WritableCatalog::RemoveEntry(const string &file_path) { | |
185 |
1/2✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
|
113 | DirectoryEntry entry; |
186 |
2/4✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 113 times.
✗ Branch 5 not taken.
|
113 | bool retval = LookupPath(PathString(file_path), &entry); |
187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | assert(retval); |
188 | |||
189 |
1/2✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
|
113 | SetDirty(); |
190 | |||
191 | // If the entry used to be a chunked file... remove the chunks | ||
192 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 113 times.
|
113 | if (entry.IsChunkedFile()) { |
193 | ✗ | RemoveFileChunks(file_path); | |
194 | } | ||
195 | |||
196 | // remove the entry itself | ||
197 |
1/2✓ Branch 2 taken 113 times.
✗ Branch 3 not taken.
|
113 | shash::Md5 path_hash = shash::Md5(shash::AsciiPtr(file_path)); |
198 | 113 | retval = | |
199 |
2/4✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 113 times.
✗ Branch 4 not taken.
|
226 | sql_unlink_->BindPathHash(path_hash) && |
200 |
2/4✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 113 times.
✗ Branch 4 not taken.
|
113 | sql_unlink_->Execute(); |
201 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | assert(retval); |
202 |
1/2✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
|
113 | sql_unlink_->Reset(); |
203 | |||
204 |
1/2✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
|
113 | delta_counters_.Decrement(entry); |
205 | 113 | } | |
206 | |||
207 | |||
208 | ✗ | void WritableCatalog::IncLinkcount(const string &path_within_group, | |
209 | const int delta) | ||
210 | { | ||
211 | ✗ | SetDirty(); | |
212 | |||
213 | ✗ | shash::Md5 path_hash = shash::Md5(shash::AsciiPtr(path_within_group)); | |
214 | |||
215 | bool retval = | ||
216 | ✗ | sql_inc_linkcount_->BindPathHash(path_hash) && | |
217 | ✗ | sql_inc_linkcount_->BindDelta(delta) && | |
218 | ✗ | sql_inc_linkcount_->Execute(); | |
219 | ✗ | assert(retval); | |
220 | ✗ | sql_inc_linkcount_->Reset(); | |
221 | } | ||
222 | |||
223 | |||
224 | 4 | void WritableCatalog::TouchEntry(const DirectoryEntryBase &entry, | |
225 | const XattrList &xattrs, | ||
226 | const shash::Md5 &path_hash) { | ||
227 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | SetDirty(); |
228 | |||
229 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | catalog::DirectoryEntry prev_entry; |
230 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | bool retval = LookupMd5Path(path_hash, &prev_entry); |
231 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
232 | |||
233 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
8 | retval = sql_touch_->BindPathHash(path_hash) && |
234 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | sql_touch_->BindDirentBase(entry); |
235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
236 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | if (xattrs.IsEmpty()) { |
237 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | retval = sql_touch_->BindXattrEmpty(); |
238 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (prev_entry.HasXattrs()) |
239 | ✗ | delta_counters_.self.xattrs--; | |
240 | } else { | ||
241 | ✗ | retval = sql_touch_->BindXattr(xattrs); | |
242 | ✗ | if (!prev_entry.HasXattrs()) | |
243 | ✗ | delta_counters_.self.xattrs++; | |
244 | } | ||
245 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
246 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | retval = sql_touch_->Execute(); |
247 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
248 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | sql_touch_->Reset(); |
249 | 4 | } | |
250 | |||
251 | |||
252 | 177 | void WritableCatalog::UpdateEntry(const DirectoryEntry &entry, | |
253 | const shash::Md5 &path_hash) { | ||
254 | 177 | SetDirty(); | |
255 | |||
256 | bool retval = | ||
257 |
0/2✗ Branch 1 not taken.
✗ Branch 2 not taken.
|
177 | sql_update_->BindPathHash(path_hash) && |
258 |
2/4✓ Branch 0 taken 177 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 177 times.
✗ Branch 4 not taken.
|
354 | sql_update_->BindDirent(entry) && |
259 |
1/2✓ Branch 1 taken 177 times.
✗ Branch 2 not taken.
|
177 | sql_update_->Execute(); |
260 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 177 times.
|
177 | assert(retval); |
261 | 177 | sql_update_->Reset(); | |
262 | 177 | } | |
263 | |||
264 | 10 | void WritableCatalog::AddFileChunk(const std::string &entry_path, | |
265 | const FileChunk &chunk) { | ||
266 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | SetDirty(); |
267 | |||
268 |
1/2✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
10 | shash::Md5 path_hash((shash::AsciiPtr(entry_path))); |
269 | |||
270 |
1/4✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
10 | LogCvmfs(kLogCatalog, kLogVerboseMsg, "adding chunk for %s from offset %ld " |
271 | "and chunk size: %ld bytes", | ||
272 | entry_path.c_str(), | ||
273 | chunk.offset(), | ||
274 | 10 | chunk.offset() + chunk.size()); | |
275 | |||
276 | 10 | delta_counters_.self.file_chunks++; | |
277 | |||
278 | bool retval = | ||
279 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | sql_chunk_insert_->BindPathHash(path_hash) && |
280 |
3/6✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
|
20 | sql_chunk_insert_->BindFileChunk(chunk) && |
281 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
10 | sql_chunk_insert_->Execute(); |
282 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | assert(retval); |
283 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | sql_chunk_insert_->Reset(); |
284 | 10 | } | |
285 | |||
286 | |||
287 | /** | ||
288 | * Removes the file chunks for a given file path | ||
289 | * @param entry_path the file path to clear from it's file chunks | ||
290 | */ | ||
291 | ✗ | void WritableCatalog::RemoveFileChunks(const std::string &entry_path) { | |
292 | ✗ | shash::Md5 path_hash((shash::AsciiPtr(entry_path))); | |
293 | bool retval; | ||
294 | |||
295 | // subtract the number of chunks from the statistics counters | ||
296 | ✗ | retval = | |
297 | ✗ | sql_chunks_count_->BindPathHash(path_hash) && | |
298 | ✗ | sql_chunks_count_->Execute(); | |
299 | ✗ | assert(retval); | |
300 | ✗ | const int chunks_count = sql_chunks_count_->GetChunkCount(); | |
301 | ✗ | delta_counters_.self.file_chunks -= chunks_count; | |
302 | ✗ | sql_chunks_count_->Reset(); | |
303 | |||
304 | // remove the chunks associated to `entry_path` | ||
305 | ✗ | retval = | |
306 | ✗ | sql_chunks_remove_->BindPathHash(path_hash) && | |
307 | ✗ | sql_chunks_remove_->Execute(); | |
308 | ✗ | assert(retval); | |
309 | ✗ | sql_chunks_remove_->Reset(); | |
310 | } | ||
311 | |||
312 | |||
313 | /** | ||
314 | * Sets the last modified time stamp of this catalog to current time. | ||
315 | */ | ||
316 | 146 | void WritableCatalog::UpdateLastModified() { | |
317 |
2/4✓ Branch 4 taken 146 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 146 times.
✗ Branch 8 not taken.
|
146 | database().SetProperty("last_modified", static_cast<uint64_t>(time(NULL))); |
318 | 146 | } | |
319 | |||
320 | |||
321 | /** | ||
322 | * Increments the revision of the catalog in the database. | ||
323 | */ | ||
324 | 46 | void WritableCatalog::IncrementRevision() { | |
325 | 46 | SetRevision(GetRevision() + 1); | |
326 | 46 | } | |
327 | |||
328 | |||
329 | 46 | void WritableCatalog::SetRevision(const uint64_t new_revision) { | |
330 |
2/4✓ Branch 3 taken 46 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 46 times.
✗ Branch 7 not taken.
|
46 | database().SetProperty("revision", new_revision); |
331 | 46 | } | |
332 | |||
333 | |||
334 | ✗ | void WritableCatalog::SetBranch(const std::string &branch_name) { | |
335 | ✗ | database().SetProperty("branch", branch_name); | |
336 | } | ||
337 | |||
338 | |||
339 | ✗ | void WritableCatalog::SetTTL(const uint64_t new_ttl) { | |
340 | ✗ | database().SetProperty("TTL", new_ttl); | |
341 | } | ||
342 | |||
343 | |||
344 | ✗ | bool WritableCatalog::SetVOMSAuthz(const std::string &voms_authz) { | |
345 | ✗ | return database().SetVOMSAuthz(voms_authz); | |
346 | } | ||
347 | |||
348 | |||
349 | /** | ||
350 | * Sets the content hash of the previous catalog revision. | ||
351 | */ | ||
352 | 46 | void WritableCatalog::SetPreviousRevision(const shash::Any &hash) { | |
353 |
2/4✓ Branch 4 taken 46 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 46 times.
✗ Branch 8 not taken.
|
46 | database().SetProperty("previous_revision", hash.ToString()); |
354 | 46 | } | |
355 | |||
356 | |||
357 | /** | ||
358 | * Moves a subtree from this catalog into a just created nested catalog. | ||
359 | */ | ||
360 | 29 | void WritableCatalog::Partition(WritableCatalog *new_nested_catalog) { | |
361 | // Create connection between parent and child catalogs | ||
362 |
3/6✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 29 times.
✗ Branch 8 not taken.
|
29 | MakeTransitionPoint(new_nested_catalog->mountpoint().ToString()); |
363 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
29 | new_nested_catalog->MakeNestedRoot(); |
364 | 29 | delta_counters_.subtree.directories++; // Root directory in nested catalog | |
365 | |||
366 | // Move the present directory tree into the newly created nested catalog | ||
367 | // if we hit nested catalog mountpoints on the way, we return them through | ||
368 | // the passed list | ||
369 | 29 | vector<string> GrandChildMountpoints; | |
370 |
3/6✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 29 times.
✗ Branch 8 not taken.
|
29 | MoveToNested(new_nested_catalog->mountpoint().ToString(), new_nested_catalog, |
371 | &GrandChildMountpoints); | ||
372 | |||
373 | // Nested catalog mountpoints found in the moved directory structure are now | ||
374 | // links to nested catalogs of the newly created nested catalog. | ||
375 | // Move these references into the new nested catalog | ||
376 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
29 | MoveCatalogsToNested(GrandChildMountpoints, new_nested_catalog); |
377 | 29 | } | |
378 | |||
379 | |||
380 | 29 | void WritableCatalog::MakeTransitionPoint(const string &mountpoint) { | |
381 | // Find the directory entry to edit | ||
382 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
29 | DirectoryEntry transition_entry; |
383 |
2/4✓ Branch 3 taken 29 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
|
29 | bool retval = LookupPath(PathString(mountpoint.data(), mountpoint.length()), |
384 | &transition_entry); | ||
385 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | assert(retval); |
386 | |||
387 |
2/4✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
|
29 | assert(transition_entry.IsDirectory() && |
388 | !transition_entry.IsNestedCatalogRoot()); | ||
389 | |||
390 | 29 | transition_entry.set_is_nested_catalog_mountpoint(true); | |
391 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
29 | UpdateEntry(transition_entry, mountpoint); |
392 | 29 | } | |
393 | |||
394 | |||
395 | 29 | void WritableCatalog::MakeNestedRoot() { | |
396 |
1/2✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
|
29 | DirectoryEntry root_entry; |
397 |
2/4✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
|
29 | bool retval = LookupPath(mountpoint(), &root_entry); |
398 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
|
29 | assert(retval); |
399 | |||
400 |
2/4✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
|
29 | assert(root_entry.IsDirectory() && !root_entry.IsNestedCatalogMountpoint()); |
401 | |||
402 | 29 | root_entry.set_is_nested_catalog_root(true); | |
403 |
3/6✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 29 times.
✗ Branch 8 not taken.
|
29 | UpdateEntry(root_entry, mountpoint().ToString()); |
404 | 29 | } | |
405 | |||
406 | |||
407 | 50 | void WritableCatalog::MoveToNestedRecursively( | |
408 | const string directory, | ||
409 | WritableCatalog *new_nested_catalog, | ||
410 | vector<string> *grand_child_mountpoints) | ||
411 | { | ||
412 | // After creating a new nested catalog we have to move all elements | ||
413 | // now contained by the new one. List and move them recursively. | ||
414 | 50 | DirectoryEntryList listing; | |
415 | 50 | const bool resolve_magic_symlinks = false; | |
416 |
2/4✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 50 times.
✗ Branch 5 not taken.
|
50 | bool retval = ListingPath(PathString(directory), &listing, |
417 | resolve_magic_symlinks); | ||
418 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | assert(retval); |
419 | |||
420 | // Go through the listing | ||
421 | 50 | XattrList empty_xattrs; | |
422 | 50 | for (DirectoryEntryList::const_iterator i = listing.begin(), | |
423 |
2/2✓ Branch 4 taken 106 times.
✓ Branch 5 taken 50 times.
|
156 | iEnd = listing.end(); i != iEnd; ++i) |
424 | { | ||
425 |
1/2✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
|
106 | const string full_path = i->GetFullPath(directory); |
426 | |||
427 | // The entries are first inserted into the new catalog | ||
428 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 106 times.
|
106 | if (i->HasXattrs()) { |
429 | ✗ | XattrList xattrs; | |
430 | ✗ | retval = LookupXattrsPath(PathString(full_path), &xattrs); | |
431 | ✗ | assert(retval); | |
432 | ✗ | assert(!xattrs.IsEmpty()); | |
433 | ✗ | new_nested_catalog->AddEntry(*i, xattrs, full_path); | |
434 | ✗ | } else { | |
435 |
1/2✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
|
106 | new_nested_catalog->AddEntry(*i, empty_xattrs, full_path); |
436 | } | ||
437 | |||
438 | // Then we check if we have some special cases: | ||
439 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 106 times.
|
106 | if (i->IsNestedCatalogMountpoint()) { |
440 | ✗ | grand_child_mountpoints->push_back(full_path); | |
441 |
2/2✓ Branch 2 taken 21 times.
✓ Branch 3 taken 85 times.
|
106 | } else if (i->IsDirectory()) { |
442 | // Recurse deeper into the directory tree | ||
443 |
2/4✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
|
21 | MoveToNestedRecursively(full_path, new_nested_catalog, |
444 | grand_child_mountpoints); | ||
445 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 85 times.
|
85 | } else if (i->IsChunkedFile()) { |
446 | ✗ | MoveFileChunksToNested(full_path, i->hash_algorithm(), | |
447 | new_nested_catalog); | ||
448 | } | ||
449 | |||
450 | // Remove the entry from the current catalog | ||
451 |
1/2✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
|
106 | RemoveEntry(full_path); |
452 | 106 | } | |
453 | 50 | } | |
454 | |||
455 | |||
456 | 29 | void WritableCatalog::MoveCatalogsToNested( | |
457 | const vector<string> &nested_catalogs, | ||
458 | WritableCatalog *new_nested_catalog) | ||
459 | { | ||
460 | 58 | for (vector<string>::const_iterator i = nested_catalogs.begin(), | |
461 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
|
58 | iEnd = nested_catalogs.end(); i != iEnd; ++i) |
462 | { | ||
463 | ✗ | shash::Any hash_nested; | |
464 | uint64_t size_nested; | ||
465 | ✗ | bool retval = FindNested(PathString(*i), &hash_nested, &size_nested); | |
466 | ✗ | assert(retval); | |
467 | |||
468 | ✗ | Catalog *attached_reference = NULL; | |
469 | ✗ | RemoveNestedCatalog(*i, &attached_reference); | |
470 | |||
471 | ✗ | new_nested_catalog->InsertNestedCatalog(*i, attached_reference, | |
472 | hash_nested, size_nested); | ||
473 | } | ||
474 | 29 | } | |
475 | |||
476 | |||
477 | ✗ | void WritableCatalog::MoveFileChunksToNested( | |
478 | const std::string &full_path, | ||
479 | const shash::Algorithms algorithm, | ||
480 | WritableCatalog *new_nested_catalog) | ||
481 | { | ||
482 | ✗ | FileChunkList chunks; | |
483 | ✗ | ListPathChunks(PathString(full_path), algorithm, &chunks); | |
484 | ✗ | assert(chunks.size() > 0); | |
485 | |||
486 | ✗ | for (unsigned i = 0; i < chunks.size(); ++i) { | |
487 | ✗ | new_nested_catalog->AddFileChunk(full_path, *chunks.AtPtr(i)); | |
488 | } | ||
489 | } | ||
490 | |||
491 | |||
492 | /** | ||
493 | * Insert a nested catalog reference into this catalog. | ||
494 | * The attached catalog object of this mountpoint can be specified (optional) | ||
495 | * This way, the in-memory representation of the catalog tree is updated, too | ||
496 | * @param mountpoint the path to the catalog to add a reference to | ||
497 | * @param attached_reference can contain a reference to the attached catalog | ||
498 | * object of mountpoint | ||
499 | * @param content_hash can be set to safe a content hash together with the | ||
500 | * reference | ||
501 | */ | ||
502 | 42 | void WritableCatalog::InsertNestedCatalog(const string &mountpoint, | |
503 | Catalog *attached_reference, | ||
504 | const shash::Any content_hash, | ||
505 | const uint64_t size) | ||
506 | { | ||
507 | 42 | const string hash_string = (!content_hash.IsNull()) ? | |
508 |
6/12✓ Branch 0 taken 13 times.
✓ Branch 1 taken 29 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 29 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 29 times.
✓ Branch 10 taken 13 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
|
42 | content_hash.ToString() : ""; |
509 | |||
510 | 42 | SqlCatalog stmt(database(), "INSERT INTO nested_catalogs (path, sha1, size) " | |
511 |
2/4✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
|
126 | "VALUES (:p, :sha1, :size);"); |
512 | bool retval = | ||
513 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | stmt.BindText(1, mountpoint) && |
514 |
2/4✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
|
42 | stmt.BindText(2, hash_string) && |
515 |
3/6✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
|
126 | stmt.BindInt64(3, size) && |
516 |
2/4✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
|
42 | stmt.Execute(); |
517 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
|
42 | assert(retval); |
518 | |||
519 | // If a reference of the in-memory object of the newly referenced | ||
520 | // catalog was passed, we add this to our own children | ||
521 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
|
42 | if (attached_reference != NULL) |
522 | ✗ | AddChild(attached_reference); | |
523 | |||
524 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | ResetNestedCatalogCacheUnprotected(); |
525 | |||
526 | 42 | delta_counters_.self.nested_catalogs++; | |
527 | 42 | } | |
528 | |||
529 | |||
530 | /** | ||
531 | * Registers a snapshot in /.cvmfs/snapshots. Note that bind mountpoints are | ||
532 | * not universally handled: in Partition and MergeIntoParent, bind mountpoint | ||
533 | * handling is missing! | ||
534 | */ | ||
535 | ✗ | void WritableCatalog::InsertBindMountpoint( | |
536 | const string &mountpoint, | ||
537 | const shash::Any content_hash, | ||
538 | const uint64_t size) | ||
539 | { | ||
540 | ✗ | SqlCatalog stmt(database(), | |
541 | "INSERT INTO bind_mountpoints (path, sha1, size) " | ||
542 | ✗ | "VALUES (:p, :sha1, :size);"); | |
543 | bool retval = | ||
544 | ✗ | stmt.BindText(1, mountpoint) && | |
545 | ✗ | stmt.BindText(2, content_hash.ToString()) && | |
546 | ✗ | stmt.BindInt64(3, size) && | |
547 | ✗ | stmt.Execute(); | |
548 | ✗ | assert(retval); | |
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * Remove a nested catalog reference from the database. | ||
554 | * If the catalog 'mountpoint' is currently attached as a child, it will be | ||
555 | * removed, too (but not detached). | ||
556 | * @param[in] mountpoint the mountpoint of the nested catalog to dereference in | ||
557 | the database | ||
558 | * @param[out] attached_reference is set to the object of the attached child or | ||
559 | * to NULL | ||
560 | */ | ||
561 | 4 | void WritableCatalog::RemoveNestedCatalog(const string &mountpoint, | |
562 | Catalog **attached_reference) | ||
563 | { | ||
564 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | shash::Any dummy; |
565 | uint64_t dummy_size; | ||
566 |
2/4✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
|
4 | bool retval = FindNested(PathString(mountpoint.data(), mountpoint.length()), |
567 | &dummy, &dummy_size); | ||
568 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
569 | |||
570 | 4 | SqlCatalog stmt(database(), | |
571 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
12 | "DELETE FROM nested_catalogs WHERE path = :p;"); |
572 | 4 | retval = | |
573 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
8 | stmt.BindText(1, mountpoint) && |
574 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | stmt.Execute(); |
575 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(retval); |
576 | |||
577 | // If the reference was successfully deleted, we also have to check whether | ||
578 | // there is also an attached reference in our in-memory data. | ||
579 | // In this case we remove the child and return it through **attached_reference | ||
580 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | Catalog *child = FindChild(PathString(mountpoint)); |
581 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if (child != NULL) |
582 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | RemoveChild(child); |
583 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (attached_reference != NULL) |
584 | ✗ | *attached_reference = child; | |
585 | |||
586 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | ResetNestedCatalogCacheUnprotected(); |
587 | |||
588 | 4 | delta_counters_.self.nested_catalogs--; | |
589 | 4 | } | |
590 | |||
591 | |||
592 | /** | ||
593 | * Unregisters a snapshot from /.cvmfs/snapshots. Note that bind mountpoints | ||
594 | * are not universally handled: in Partition and MergeIntoParent, bind | ||
595 | * mountpoint handling is missing! | ||
596 | */ | ||
597 | ✗ | void WritableCatalog::RemoveBindMountpoint(const std::string &mountpoint) { | |
598 | ✗ | shash::Any dummy; | |
599 | uint64_t dummy_size; | ||
600 | ✗ | bool retval = FindNested(PathString(mountpoint.data(), mountpoint.length()), | |
601 | &dummy, &dummy_size); | ||
602 | ✗ | assert(retval); | |
603 | |||
604 | ✗ | SqlCatalog stmt(database(), | |
605 | ✗ | "DELETE FROM bind_mountpoints WHERE path = :p;"); | |
606 | ✗ | retval = | |
607 | ✗ | stmt.BindText(1, mountpoint) && | |
608 | ✗ | stmt.Execute(); | |
609 | ✗ | assert(retval); | |
610 | } | ||
611 | |||
612 | |||
613 | /** | ||
614 | * Updates the link to a nested catalog in the database. | ||
615 | * @param path the path of the nested catalog to update | ||
616 | * @param hash the hash to set the given nested catalog link to | ||
617 | * @param size the uncompressed catalog database file size | ||
618 | * @param child_counters the statistics counters of the nested catalog | ||
619 | */ | ||
620 | 26 | void WritableCatalog::UpdateNestedCatalog(const std::string &path, | |
621 | const shash::Any &hash, | ||
622 | const uint64_t size, | ||
623 | const DeltaCounters &child_counters) { | ||
624 | 26 | MutexLockGuard guard(lock_); | |
625 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | SetDirty(); |
626 | |||
627 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | child_counters.PopulateToParent(&delta_counters_); |
628 | |||
629 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | const string hash_str = hash.ToString(); |
630 | const string sql = "UPDATE nested_catalogs SET sha1 = :sha1, size = :size " | ||
631 |
1/2✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
|
26 | "WHERE path = :path;"; |
632 |
1/2✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
|
26 | SqlCatalog stmt(database(), sql); |
633 | |||
634 | bool retval = | ||
635 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | stmt.BindText(1, hash_str) && |
636 |
2/4✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
|
26 | stmt.BindInt64(2, size) && |
637 |
3/6✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
|
78 | stmt.BindText(3, path) && |
638 |
2/4✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
✗ Branch 4 not taken.
|
26 | stmt.Execute(); |
639 | |||
640 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | ResetNestedCatalogCacheUnprotected(); |
641 | |||
642 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | assert(retval); |
643 | 26 | } | |
644 | |||
645 | |||
646 | 1 | void WritableCatalog::MergeIntoParent() { | |
647 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | assert(!IsRoot() && HasParent()); |
648 | 1 | WritableCatalog *parent = GetWritableParent(); | |
649 | |||
650 | 1 | CopyToParent(); | |
651 | |||
652 | // Copy the nested catalog references | ||
653 | 1 | CopyCatalogsToParent(); | |
654 | |||
655 | // Fix counters in parent | ||
656 | 1 | delta_counters_.PopulateToParent(&parent->delta_counters_); | |
657 | 1 | Counters &counters = GetWritableCounters(); | |
658 | 1 | counters.ApplyDelta(delta_counters_); | |
659 | 1 | counters.MergeIntoParent(&parent->delta_counters_); | |
660 | |||
661 | // Remove the nested catalog reference for this nested catalog. | ||
662 | // From now on this catalog will be dangling! | ||
663 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | parent->RemoveNestedCatalog(this->mountpoint().ToString(), NULL); |
664 | 1 | } | |
665 | |||
666 | |||
667 | ✗ | void WritableCatalog::RemoveFromParent() { | |
668 | ✗ | assert(!IsRoot() && HasParent()); | |
669 | ✗ | WritableCatalog *parent = GetWritableParent(); | |
670 | |||
671 | // Remove the nested catalog reference for this nested catalog. | ||
672 | // From now on this catalog will be dangling! | ||
673 | ✗ | parent->RemoveNestedCatalog(this->mountpoint().ToString(), NULL); | |
674 | ✗ | parent->delta_counters_.RemoveFromSubtree( | |
675 | ✗ | Counters::Diff(Counters(), GetCounters())); | |
676 | } | ||
677 | |||
678 | |||
679 | 1 | void WritableCatalog::CopyCatalogsToParent() { | |
680 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | WritableCatalog *parent = GetWritableParent(); |
681 | |||
682 | // Obtain a list of all nested catalog references | ||
683 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | const NestedCatalogList nested_catalog_references = ListOwnNestedCatalogs(); |
684 | |||
685 | // Go through the list and update the databases | ||
686 | // simultaneously we are checking if the referenced catalogs are currently | ||
687 | // attached and update the in-memory data structures as well | ||
688 | 2 | for (NestedCatalogList::const_iterator i = nested_catalog_references.begin(), | |
689 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
2 | iEnd = nested_catalog_references.end(); i != iEnd; ++i) |
690 | { | ||
691 | ✗ | Catalog *child = FindChild(i->mountpoint); | |
692 | ✗ | parent->InsertNestedCatalog( | |
693 | ✗ | i->mountpoint.ToString(), child, i->hash, i->size); | |
694 | ✗ | parent->delta_counters_.self.nested_catalogs--; // Will be fixed later | |
695 | } | ||
696 | 1 | } | |
697 | |||
698 | 1 | void WritableCatalog::CopyToParent() { | |
699 | // We could simply copy all entries from this database to the 'other' database | ||
700 | // BUT: 1. this would create collisions in hardlink group IDs. | ||
701 | // therefore we first update all hardlink group IDs to fit behind the | ||
702 | // ones in the 'other' database | ||
703 | // 2. the root entry of the nested catalog is present twice: | ||
704 | // 1. in the parent directory (as mount point) and | ||
705 | // 2. in the nested catalog (as root entry) | ||
706 | // therefore we delete the mount point from the parent before merging | ||
707 | |||
708 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | WritableCatalog *parent = GetWritableParent(); |
709 | |||
710 | // Update hardlink group IDs in this nested catalog. | ||
711 | // To avoid collisions we add the maximal present hardlink group ID in parent | ||
712 | // to all hardlink group IDs in the nested catalog. | ||
713 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | const uint64_t offset = static_cast<uint64_t>(parent->GetMaxLinkId()) << 32; |
714 | const string update_link_ids = | ||
715 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | "UPDATE catalog SET hardlinks = hardlinks + " + StringifyInt(offset) + |
716 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | " WHERE hardlinks > (1 << 32);"; |
717 | |||
718 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | SqlCatalog sql_update_link_ids(database(), update_link_ids); |
719 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | bool retval = sql_update_link_ids.Execute(); |
720 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
721 | |||
722 | // Remove the nested catalog root. | ||
723 | // It is already present in the parent. | ||
724 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | RemoveEntry(this->mountpoint().ToString()); |
725 | |||
726 | // Now copy all DirectoryEntries to the 'other' catalog. | ||
727 | // There will be no data collisions, as we resolved them beforehand | ||
728 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (dirty_) |
729 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Commit(); |
730 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (parent->dirty_) |
731 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | parent->Commit(); |
732 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
2 | SqlCatalog sql_attach(database(), "ATTACH '" + parent->database_path() + "' " |
733 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | "AS other;"); |
734 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | retval = sql_attach.Execute(); |
735 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
736 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
2 | retval = SqlCatalog(database(), "INSERT INTO other.catalog " |
737 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | "SELECT * FROM main.catalog;").Execute(); |
738 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
739 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
2 | retval = SqlCatalog(database(), "INSERT INTO other.chunks " |
740 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | "SELECT * FROM main.chunks;").Execute(); |
741 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
742 |
3/6✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | retval = SqlCatalog(database(), "DETACH other;").Execute(); |
743 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
744 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | parent->SetDirty(); |
745 | |||
746 | // Change the just copied nested catalog root to an ordinary directory | ||
747 | // (the nested catalog is merged into it's parent) | ||
748 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | DirectoryEntry old_root_entry; |
749 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | retval = parent->LookupPath(this->mountpoint(), &old_root_entry); |
750 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert(retval); |
751 | |||
752 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | assert(old_root_entry.IsDirectory() && |
753 | old_root_entry.IsNestedCatalogMountpoint() && | ||
754 | !old_root_entry.IsNestedCatalogRoot()); | ||
755 | |||
756 | // Remove the nested catalog root mark | ||
757 | 1 | old_root_entry.set_is_nested_catalog_mountpoint(false); | |
758 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | parent->UpdateEntry(old_root_entry, this->mountpoint().ToString()); |
759 | 1 | } | |
760 | |||
761 | |||
762 | /** | ||
763 | * Writes delta_counters_ to the database. | ||
764 | */ | ||
765 | 75 | void WritableCatalog::UpdateCounters() { | |
766 |
1/4✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
150 | const bool retval = delta_counters_.WriteToDatabase(database()) && |
767 |
1/2✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
|
75 | ReadCatalogCounters(); |
768 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | assert(retval); |
769 | 75 | } | |
770 | |||
771 | |||
772 | /** | ||
773 | * Checks if the database of this catalogs needs cleanup and defragments it | ||
774 | * if necessary | ||
775 | */ | ||
776 | 46 | void WritableCatalog::VacuumDatabaseIfNecessary() { | |
777 | 46 | const CatalogDatabase &db = database(); | |
778 | 46 | bool needs_defragmentation = false; | |
779 | 46 | double ratio = 0.0; | |
780 | 46 | std::string reason; | |
781 | |||
782 |
2/4✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 46 times.
|
46 | if ((ratio = db.GetFreePageRatio()) > kMaximalFreePageRatio) { |
783 | ✗ | needs_defragmentation = true; | |
784 | ✗ | reason = "free pages"; | |
785 |
3/4✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 34 times.
|
46 | } else if ((ratio = db.GetRowIdWasteRatio()) > kMaximalRowIdWasteRatio) { |
786 | 12 | needs_defragmentation = true; | |
787 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | reason = "wasted row IDs"; |
788 | } | ||
789 | |||
790 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 34 times.
|
46 | if (needs_defragmentation) { |
791 |
3/14✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
27 | LogCvmfs(kLogCatalog, kLogStdout | kLogNoLinebreak, |
792 | "Note: Catalog at %s gets defragmented (%.2f%% %s)... ", | ||
793 |
3/6✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 9 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
27 | (IsRoot()) ? "/" : mountpoint().c_str(), |
794 | ratio * 100.0, | ||
795 | reason.c_str()); | ||
796 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
|
12 | if (!db.Vacuum()) { |
797 | ✗ | PANIC(kLogStderr, "failed (SQLite: %s)", db.GetLastErrorMsg().c_str()); | |
798 | } | ||
799 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | LogCvmfs(kLogCatalog, kLogStdout, "done"); |
800 | } | ||
801 | 46 | } | |
802 | |||
803 | } // namespace catalog | ||
804 |