Directory: | cvmfs/ |
---|---|
File: | cvmfs/sync_item.cc |
Date: | 2024-04-28 02:33:07 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 59 | 256 | 23.0% |
Branches: | 29 | 360 | 8.1% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM file system | ||
3 | */ | ||
4 | |||
5 | #include "sync_item.h" | ||
6 | |||
7 | |||
8 | #if !defined(__APPLE__) | ||
9 | #include <sys/sysmacros.h> | ||
10 | #endif // __APPLE__ | ||
11 | |||
12 | #include <cerrno> | ||
13 | #include <vector> | ||
14 | |||
15 | #include "duplex_libarchive.h" | ||
16 | #include "ingestion/ingestion_source.h" | ||
17 | #include "sync_mediator.h" | ||
18 | #include "sync_union.h" | ||
19 | #include "util/exception.h" | ||
20 | |||
21 | using namespace std; // NOLINT | ||
22 | |||
23 | namespace publish { | ||
24 | |||
25 | ✗ | SyncItem::SyncItem() : | |
26 | ✗ | rdonly_type_(static_cast<SyncItemType>(0)), | |
27 | ✗ | graft_size_(-1), | |
28 | ✗ | scratch_type_(static_cast<SyncItemType>(0)), | |
29 | ✗ | union_engine_(NULL), | |
30 | ✗ | whiteout_(false), | |
31 | ✗ | opaque_(false), | |
32 | ✗ | masked_hardlink_(false), | |
33 | ✗ | has_catalog_marker_(false), | |
34 | ✗ | valid_graft_(false), | |
35 | ✗ | graft_marker_present_(false), | |
36 | ✗ | external_data_(false), | |
37 | ✗ | direct_io_(false), | |
38 | ✗ | graft_chunklist_(NULL), | |
39 | ✗ | compression_algorithm_(zlib::kZlibDefault), | |
40 | ✗ | has_compression_algorithm_(false) {} | |
41 | |||
42 | 19 | SyncItem::SyncItem(const std::string &relative_parent_path, | |
43 | const std::string &filename, | ||
44 | const SyncUnion *union_engine, | ||
45 | 19 | const SyncItemType entry_type) : | |
46 | 19 | rdonly_type_(kItemUnknown), | |
47 | 19 | graft_size_(-1), | |
48 | 19 | scratch_type_(entry_type), | |
49 | 19 | filename_(filename), | |
50 | 19 | union_engine_(union_engine), | |
51 | 19 | whiteout_(false), | |
52 | 19 | opaque_(false), | |
53 | 19 | masked_hardlink_(false), | |
54 | 19 | has_catalog_marker_(false), | |
55 | 19 | valid_graft_(false), | |
56 | 19 | graft_marker_present_(false), | |
57 | 19 | external_data_(false), | |
58 | 19 | direct_io_(false), | |
59 |
1/2✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
|
19 | relative_parent_path_(relative_parent_path), |
60 | 19 | graft_chunklist_(NULL), | |
61 | 19 | compression_algorithm_(zlib::kZlibDefault), | |
62 | 38 | has_compression_algorithm_(false) { | |
63 | 19 | content_hash_.algorithm = shash::kAny; | |
64 | 19 | } | |
65 | |||
66 | 38 | SyncItem::~SyncItem() { | |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
38 | delete graft_chunklist_; |
68 | } | ||
69 | |||
70 | |||
71 | ✗ | SyncItemType SyncItem::GetGenericFiletype(const SyncItem::EntryStat &stat) const | |
72 | { | ||
73 | ✗ | const SyncItemType type = stat.GetSyncItemType(); | |
74 | ✗ | if (type == kItemUnknown) { | |
75 | ✗ | PANIC(kLogStderr, | |
76 | "[WARNING] '%s' has an unsupported file type (st_mode: %d errno: %d)", | ||
77 | GetRelativePath().c_str(), stat.stat.st_mode, stat.error_code); | ||
78 | } | ||
79 | ✗ | return type; | |
80 | } | ||
81 | |||
82 | |||
83 | 17 | SyncItemType SyncItem::GetRdOnlyFiletype() const { | |
84 | 17 | StatRdOnly(); | |
85 | // file could not exist in read-only branch, or a regular file could have | ||
86 | // been replaced by a directory in the read/write branch, like: | ||
87 | // rdonly: | ||
88 | // /foo/bar/regular_file <-- ENOTDIR when asking for (.../is_dir_now) | ||
89 | // r/w: | ||
90 | // /foo/bar/regular_file/ | ||
91 | // /foo/bar/regular_file/is_dir_now | ||
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | if (rdonly_stat_.error_code == ENOENT || |
93 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
17 | rdonly_stat_.error_code == ENOTDIR) return kItemNew; |
94 | ✗ | return GetGenericFiletype(rdonly_stat_); | |
95 | } | ||
96 | |||
97 | |||
98 | ✗ | SyncItemType SyncItemNative::GetScratchFiletype() const { | |
99 | ✗ | StatScratch(/* refresh= */ false); | |
100 | ✗ | if (scratch_stat_.error_code != 0) { | |
101 | ✗ | PANIC(kLogStderr, "[WARNING] Failed to stat() '%s' in scratch. (errno: %s)", | |
102 | GetRelativePath().c_str(), scratch_stat_.error_code); | ||
103 | } | ||
104 | |||
105 | ✗ | return GetGenericFiletype(scratch_stat_); | |
106 | } | ||
107 | |||
108 | ✗ | SyncItemType SyncItem::GetUnionFiletype() const { | |
109 | ✗ | StatUnion(); | |
110 | ✗ | if (union_stat_.error_code == ENOENT || union_stat_.error_code == ENOTDIR) | |
111 | ✗ | return kItemUnknown; | |
112 | ✗ | return GetGenericFiletype(union_stat_); | |
113 | } | ||
114 | |||
115 | 10 | bool SyncItemNative::IsType(const SyncItemType expected_type) const { | |
116 |
2/6✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
|
10 | if (filename().substr(0, 12) == ".cvmfsgraft-") { |
117 | ✗ | scratch_type_ = kItemMarker; | |
118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | } else if (scratch_type_ == kItemUnknown) { |
119 | ✗ | scratch_type_ = GetScratchFiletype(); | |
120 | } | ||
121 | 10 | return scratch_type_ == expected_type; | |
122 | } | ||
123 | |||
124 | ✗ | void SyncItem::MarkAsWhiteout(const std::string &actual_filename) { | |
125 | ✗ | StatScratch(/* refresh= */ true); | |
126 | // Mark the file as whiteout entry and strip the whiteout prefix | ||
127 | ✗ | whiteout_ = true; | |
128 | ✗ | filename_ = actual_filename; | |
129 | |||
130 | // Find the entry in the repository | ||
131 | ✗ | StatRdOnly(true); // <== refreshing the stat (filename might have changed) | |
132 | |||
133 | ✗ | const SyncItemType deleted_type = (rdonly_stat_.error_code == 0) | |
134 | ✗ | ? GetRdOnlyFiletype() | |
135 | ✗ | : kItemUnknown; | |
136 | |||
137 | ✗ | rdonly_type_ = deleted_type; | |
138 | ✗ | scratch_type_ = deleted_type; | |
139 | |||
140 | ✗ | if (deleted_type == kItemUnknown) { | |
141 | // Marking a SyncItem as 'whiteout' but no file to be removed found: This | ||
142 | // should not happen (actually AUFS prevents users from creating whiteouts) | ||
143 | // but can be provoked through an AUFS 'bug' (see test 593 or CVM-880). | ||
144 | // --> Warn the user, continue with kItemUnknown and cross your fingers! | ||
145 | ✗ | PrintWarning("'" + GetRelativePath() + | |
146 | "' should be deleted, but was not found in repository."); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | |||
151 | ✗ | void SyncItem::MarkAsOpaqueDirectory() { | |
152 | ✗ | assert(IsDirectory()); | |
153 | ✗ | opaque_ = true; | |
154 | } | ||
155 | |||
156 | |||
157 | ✗ | unsigned int SyncItem::GetRdOnlyLinkcount() const { | |
158 | ✗ | StatRdOnly(); | |
159 | ✗ | return rdonly_stat_.stat.st_nlink; | |
160 | } | ||
161 | |||
162 | |||
163 | ✗ | uint64_t SyncItem::GetRdOnlyInode() const { | |
164 | ✗ | StatRdOnly(); | |
165 | ✗ | return rdonly_stat_.stat.st_ino; | |
166 | } | ||
167 | |||
168 | |||
169 | ✗ | unsigned int SyncItem::GetUnionLinkcount() const { | |
170 | ✗ | StatUnion(); | |
171 | ✗ | return union_stat_.stat.st_nlink; | |
172 | } | ||
173 | |||
174 | |||
175 | ✗ | uint64_t SyncItem::GetUnionInode() const { | |
176 | ✗ | StatUnion(); | |
177 | ✗ | return union_stat_.stat.st_ino; | |
178 | } | ||
179 | |||
180 | ✗ | uint64_t SyncItem::GetScratchSize() const { | |
181 | ✗ | StatScratch(/* refresh= */ false); | |
182 | ✗ | return scratch_stat_.stat.st_size; | |
183 | } | ||
184 | |||
185 | ✗ | uint64_t SyncItem::GetRdOnlySize() const { | |
186 | ✗ | StatRdOnly(); | |
187 | ✗ | return rdonly_stat_.stat.st_size; | |
188 | } | ||
189 | |||
190 | ✗ | IngestionSource *SyncItemNative::CreateIngestionSource() const { | |
191 | ✗ | return new FileIngestionSource(GetUnionPath()); | |
192 | } | ||
193 | |||
194 | 22 | void SyncItem::StatGeneric(const string &path, | |
195 | EntryStat *info, | ||
196 | const bool refresh) { | ||
197 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
22 | if (info->obtained && !refresh) return; |
198 | 22 | int retval = platform_lstat(path.c_str(), &info->stat); | |
199 |
1/2✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
|
22 | info->error_code = (retval != 0) ? errno : 0; |
200 | 22 | info->obtained = true; | |
201 | } | ||
202 | |||
203 | |||
204 | ✗ | catalog::DirectoryEntryBase SyncItemNative::CreateBasicCatalogDirent() const { | |
205 | ✗ | catalog::DirectoryEntryBase dirent; | |
206 | |||
207 | // inode and parent inode is determined at runtime of client | ||
208 | ✗ | dirent.inode_ = catalog::DirectoryEntry::kInvalidInode; | |
209 | |||
210 | // this might mask the actual link count in case hardlinks are not supported | ||
211 | // (i.e. on setups using OverlayFS) | ||
212 | ✗ | dirent.linkcount_ = HasHardlinks() ? this->GetUnionStat().st_nlink : 1; | |
213 | |||
214 | ✗ | dirent.mode_ = this->GetUnionStat().st_mode; | |
215 | ✗ | dirent.uid_ = this->GetUnionStat().st_uid; | |
216 | ✗ | dirent.gid_ = this->GetUnionStat().st_gid; | |
217 | ✗ | dirent.size_ = graft_size_ > -1 ? graft_size_ : | |
218 | ✗ | this->GetUnionStat().st_size; | |
219 | ✗ | dirent.mtime_ = this->GetUnionStat().st_mtime; | |
220 | ✗ | dirent.checksum_ = this->GetContentHash(); | |
221 | ✗ | dirent.is_external_file_ = this->IsExternalData(); | |
222 | ✗ | dirent.is_direct_io_ = this->IsDirectIo(); | |
223 | ✗ | dirent.compression_algorithm_ = this->GetCompressionAlgorithm(); | |
224 | |||
225 | ✗ | dirent.name_.Assign(filename().data(), filename().length()); | |
226 | |||
227 | ✗ | if (this->IsSymlink()) { | |
228 | char slnk[PATH_MAX+1]; | ||
229 | const ssize_t length = | ||
230 | ✗ | readlink((this->GetUnionPath()).c_str(), slnk, PATH_MAX); | |
231 | ✗ | assert(length >= 0); | |
232 | ✗ | dirent.symlink_.Assign(slnk, length); | |
233 | } | ||
234 | |||
235 | ✗ | if (this->IsCharacterDevice() || this->IsBlockDevice()) { | |
236 | ✗ | dirent.size_ = makedev(GetRdevMajor(), GetRdevMinor()); | |
237 | } | ||
238 | |||
239 | ✗ | return dirent; | |
240 | } | ||
241 | |||
242 | |||
243 | 17 | std::string SyncItem::GetRdOnlyPath() const { | |
244 |
1/2✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
|
17 | const string relative_path = GetRelativePath().empty() ? |
245 |
5/18✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 17 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 17 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 17 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 17 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
34 | "" : "/" + GetRelativePath(); |
246 |
2/4✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
|
51 | return union_engine_->rdonly_path() + relative_path; |
247 | 17 | } | |
248 | |||
249 | 14 | std::string SyncItem::GetUnionPath() const { | |
250 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | const string relative_path = GetRelativePath().empty() ? |
251 |
5/18✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 14 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
|
28 | "" : "/" + GetRelativePath(); |
252 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
42 | return union_engine_->union_path() + relative_path; |
253 | 14 | } | |
254 | |||
255 | ✗ | std::string SyncItem::GetScratchPath() const { | |
256 | ✗ | const string relative_path = GetRelativePath().empty() ? | |
257 | ✗ | "" : "/" + GetRelativePath(); | |
258 | ✗ | return union_engine_->scratch_path() + relative_path; | |
259 | // return union_engine_->scratch_path() + filename(); | ||
260 | } | ||
261 | |||
262 | 5 | void SyncItem::CheckMarkerFiles() { | |
263 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
|
5 | if (IsRegularFile()) { |
264 | ✗ | CheckGraft(); | |
265 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
5 | } else if (IsDirectory()) { |
266 | 5 | CheckCatalogMarker(); | |
267 | } | ||
268 | 5 | } | |
269 | |||
270 | 5 | void SyncItem::CheckCatalogMarker() { | |
271 |
2/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
|
5 | std::string path(GetUnionPath() + "/.cvmfscatalog"); |
272 | 5 | EntryStat stat; | |
273 | 5 | StatGeneric(path, &stat, false); | |
274 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (stat.error_code) { |
275 | 5 | has_catalog_marker_ = false; | |
276 | 5 | return; | |
277 | } | ||
278 | ✗ | if (stat.GetSyncItemType() == kItemFile) { | |
279 | ✗ | has_catalog_marker_ = true; | |
280 | ✗ | return; | |
281 | } | ||
282 | ✗ | PANIC(kLogStderr, "Error: '%s' is not a regular file.", path.c_str()); | |
283 | 5 | } | |
284 | |||
285 | |||
286 | ✗ | std::string SyncItem::GetGraftMarkerPath() const { | |
287 | ✗ | return union_engine_->scratch_path() + "/" + | |
288 | ✗ | ((relative_parent_path_.empty()) ? | |
289 | ✗ | ".cvmfsgraft-" + filename_ : | |
290 | ✗ | relative_parent_path_ + (filename_.empty() ? "" : | |
291 | ✗ | ("/.cvmfsgraft-" + filename_))); | |
292 | } | ||
293 | |||
294 | ✗ | void SyncItem::CheckGraft() { | |
295 | ✗ | valid_graft_ = false; | |
296 | ✗ | bool found_checksum = false; | |
297 | ✗ | std::string checksum_type; | |
298 | ✗ | std::string checksum_value; | |
299 | ✗ | std::string graftfile = GetGraftMarkerPath(); | |
300 | ✗ | LogCvmfs(kLogFsTraversal, kLogDebug, "Checking potential graft path %s.", | |
301 | graftfile.c_str()); | ||
302 | ✗ | FILE *fp = fopen(graftfile.c_str(), "r"); | |
303 | ✗ | if (fp == NULL) { | |
304 | // This sync item can be a file from a removed directory tree on overlayfs. | ||
305 | // In this case, the entire tree is missing on the scratch directory and | ||
306 | // the errno is ENOTDIR. | ||
307 | ✗ | if ((errno != ENOENT) && (errno != ENOTDIR)) { | |
308 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Unable to open graft file " | |
309 | "(%s): %s (errno=%d)", | ||
310 | ✗ | graftfile.c_str(), strerror(errno), errno); | |
311 | } | ||
312 | ✗ | return; | |
313 | } | ||
314 | ✗ | graft_marker_present_ = true; | |
315 | ✗ | valid_graft_ = true; | |
316 | ✗ | std::string line; | |
317 | ✗ | std::vector<std::string> contents; | |
318 | |||
319 | ✗ | std::vector<off_t> chunk_offsets; | |
320 | ✗ | std::vector<shash::Any> chunk_checksums; | |
321 | |||
322 | ✗ | while (GetLineFile(fp, &line)) { | |
323 | ✗ | std::string trimmed_line = Trim(line); | |
324 | |||
325 | ✗ | if (!trimmed_line.size()) {continue;} | |
326 | ✗ | if (trimmed_line[0] == '#') {continue;} | |
327 | |||
328 | ✗ | std::vector<std::string> info = SplitStringBounded(2, trimmed_line, '='); | |
329 | |||
330 | ✗ | if (info.size() != 2) { | |
331 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Invalid line in graft file: %s", | |
332 | trimmed_line.c_str()); | ||
333 | } | ||
334 | ✗ | info[0] = Trim(info[0]); | |
335 | ✗ | info[1] = Trim(info[1]); | |
336 | ✗ | if (info[0] == "size") { | |
337 | uint64_t tmp_size; | ||
338 | ✗ | if (!String2Uint64Parse(info[1], &tmp_size)) { | |
339 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Failed to parse value of %s " | |
340 | "to integer: %s (errno=%d)", trimmed_line.c_str(), | ||
341 | ✗ | strerror(errno), errno); | |
342 | ✗ | continue; | |
343 | } | ||
344 | ✗ | graft_size_ = tmp_size; | |
345 | ✗ | } else if (info[0] == "checksum") { | |
346 | ✗ | std::string hash_str = info[1]; | |
347 | ✗ | shash::HexPtr hashP(hash_str); | |
348 | ✗ | if (hashP.IsValid()) { | |
349 | ✗ | content_hash_ = shash::MkFromHexPtr(hashP); | |
350 | ✗ | found_checksum = true; | |
351 | } else { | ||
352 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Invalid checksum value: %s.", | |
353 | ✗ | info[1].c_str()); | |
354 | } | ||
355 | ✗ | continue; | |
356 | ✗ | } else if (info[0] == "chunk_offsets") { | |
357 | ✗ | std::vector<std::string> offsets = SplitString(info[1], ','); | |
358 | ✗ | for (std::vector<std::string>::const_iterator it = offsets.begin(); | |
359 | ✗ | it != offsets.end(); it++) | |
360 | { | ||
361 | uint64_t val; | ||
362 | ✗ | if (!String2Uint64Parse(*it, &val)) { | |
363 | ✗ | valid_graft_ = false; | |
364 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Invalid chunk offset: %s.", | |
365 | it->c_str()); | ||
366 | ✗ | break; | |
367 | } | ||
368 | ✗ | chunk_offsets.push_back(val); | |
369 | } | ||
370 | ✗ | } else if (info[0] == "chunk_checksums") { | |
371 | ✗ | std::vector<std::string> csums = SplitString(info[1], ','); | |
372 | ✗ | for (std::vector<std::string>::const_iterator it = csums.begin(); | |
373 | ✗ | it != csums.end(); it++) | |
374 | { | ||
375 | ✗ | shash::HexPtr hashP(*it); | |
376 | ✗ | if (hashP.IsValid()) { | |
377 | ✗ | chunk_checksums.push_back(shash::MkFromHexPtr(hashP)); | |
378 | } else { | ||
379 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Invalid chunk checksum " | |
380 | "value: %s.", it->c_str()); | ||
381 | ✗ | valid_graft_ = false; | |
382 | ✗ | break; | |
383 | } | ||
384 | } | ||
385 | ✗ | } else if (info[0] == "compression") { | |
386 | ✗ | SetCompressionAlgorithm(zlib::ParseCompressionAlgorithm(info[1])); | |
387 | } | ||
388 | } | ||
389 | ✗ | if (!feof(fp)) { | |
390 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Unable to read from catalog " | |
391 | "marker (%s): %s (errno=%d)", | ||
392 | ✗ | graftfile.c_str(), strerror(errno), errno); | |
393 | } | ||
394 | ✗ | fclose(fp); | |
395 | ✗ | valid_graft_ = valid_graft_ && (graft_size_ > -1) && found_checksum | |
396 | ✗ | && (chunk_checksums.size() == chunk_offsets.size()); | |
397 | |||
398 | ✗ | if (!valid_graft_ || chunk_offsets.empty()) | |
399 | ✗ | return; | |
400 | |||
401 | // Parse chunks | ||
402 | ✗ | graft_chunklist_ = new FileChunkList(chunk_offsets.size()); | |
403 | ✗ | off_t last_offset = chunk_offsets[0]; | |
404 | ✗ | if (last_offset != 0) { | |
405 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "First chunk offset must be 0" | |
406 | " (in graft marker %s).", graftfile.c_str()); | ||
407 | ✗ | valid_graft_ = false; | |
408 | } | ||
409 | ✗ | for (unsigned idx = 1; idx < chunk_offsets.size(); idx++) { | |
410 | ✗ | off_t cur_offset = chunk_offsets[idx]; | |
411 | ✗ | if (last_offset >= cur_offset) { | |
412 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Chunk offsets must be sorted " | |
413 | "in strictly increasing order (in graft marker %s).", | ||
414 | graftfile.c_str()); | ||
415 | ✗ | valid_graft_ = false; | |
416 | ✗ | break; | |
417 | } | ||
418 | ✗ | size_t cur_size = cur_offset - last_offset; | |
419 | ✗ | graft_chunklist_->PushBack(FileChunk(chunk_checksums[idx - 1], | |
420 | last_offset, | ||
421 | cur_size)); | ||
422 | ✗ | last_offset = cur_offset; | |
423 | } | ||
424 | ✗ | if (graft_size_ <= last_offset) { | |
425 | ✗ | LogCvmfs(kLogFsTraversal, kLogWarning, "Last offset must be strictly " | |
426 | "less than total file size (in graft marker %s).", | ||
427 | graftfile.c_str()); | ||
428 | ✗ | valid_graft_ = false; | |
429 | } | ||
430 | ✗ | graft_chunklist_->PushBack(FileChunk(chunk_checksums.back(), | |
431 | last_offset, | ||
432 | ✗ | graft_size_ - last_offset)); | |
433 | } | ||
434 | |||
435 | } // namespace publish | ||
436 |