GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/sync_item.h
Date: 2026-09-13 02:40:16
Exec Total Coverage
Lines: 25 105 23.8%
Branches: 12 85 14.1%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System
3 */
4
5 #ifndef CVMFS_SYNC_ITEM_H_
6 #define CVMFS_SYNC_ITEM_H_
7
8 #include <sys/types.h>
9
10 #if !defined(__APPLE__)
11 #include <sys/sysmacros.h>
12 #endif // __APPLE__
13
14 #include <cstring>
15 #include <map>
16 #include <string>
17
18 #include "crypto/hash.h"
19 #include "directory_entry.h"
20 #include "duplex_libarchive.h"
21 #include "file_chunk.h"
22 #include "util/platform.h"
23 #include "util/shared_ptr.h"
24 #include "util/string.h"
25
26 class IngestionSource;
27
28 namespace publish {
29
30 enum SyncItemType {
31 kItemDir,
32 kItemFile,
33 kItemSymlink,
34 kItemCharacterDevice,
35 kItemBlockDevice,
36 kItemFifo,
37 kItemSocket,
38 kItemNew,
39 kItemMarker,
40 kItemUnknown,
41 };
42
43 class SyncUnion;
44 /**
45 * Every directory entry emitted by the FileSystemTraversal is wrapped in a
46 * SyncItem structure by the factory method SyncUnion::CreateSyncItem().
47 *
48 * Since we are dealing with a union file system setup, this class represents
49 * potentially three concrete files:
50 * - <read-only path>/<filename> | cf. rdonly_stat_
51 * - <scratch (read-write) branch>/<filename> | cf. scratch_stat_
52 * - <union volume path>/<filename> | cf. union_stat_
53 *
54 * This class caches stat calls to the underlying files in different branches of
55 * the union file system and hides some interpretation details.
56 */
57 class SyncItem {
58 // only SyncUnion can create SyncItems (see SyncUnion::CreateSyncItem).
59 // SyncUnionTarball can create SyncItemTar and SyncItemDummyDir.
60
61 public:
62 SyncItem();
63 virtual ~SyncItem();
64
65 82 inline bool IsDirectory() const { return IsType(kItemDir); }
66 inline bool WasDirectory() const { return WasType(kItemDir); }
67 58 inline bool IsRegularFile() const { return IsType(kItemFile); }
68 inline bool WasRegularFile() const { return WasType(kItemFile); }
69 2 inline bool IsSymlink() const { return IsType(kItemSymlink); }
70 inline bool WasSymlink() const { return WasType(kItemSymlink); }
71 38 inline bool IsNew() const { return WasType(kItemNew); }
72 inline bool IsTouched() const {
73 return (GetRdOnlyFiletype() == GetUnionFiletype())
74 && (GetRdOnlyFiletype() == GetScratchFiletype())
75 && (GetUnionFiletype() == GetScratchFiletype());
76 }
77 inline bool IsCharacterDevice() const { return IsType(kItemCharacterDevice); }
78 inline bool IsBlockDevice() const { return IsType(kItemBlockDevice); }
79 inline bool IsFifo() const { return IsType(kItemFifo); }
80 inline bool IsSocket() const { return IsType(kItemSocket); }
81 inline bool IsGraftMarker() const { return IsType(kItemMarker); }
82 inline bool IsExternalData() const { return external_data_; }
83 inline bool IsDirectIo() const { return direct_io_; }
84 inline bool IsVolatile() const { return volatile_; }
85
86 20 inline bool IsWhiteout() const { return whiteout_; }
87 inline bool IsCatalogMarker() const { return filename_ == ".cvmfscatalog"; }
88 inline bool IsOpaqueDirectory() const { return IsDirectory() && opaque_; }
89
90 inline bool IsSpecialFile() const {
91 return IsCharacterDevice() || IsBlockDevice() || IsFifo() || IsSocket();
92 }
93 inline bool WasSpecialFile() const {
94 return WasType(kItemCharacterDevice) || WasType(kItemBlockDevice)
95 || WasType(kItemFifo) || WasType(kItemSocket);
96 }
97 inline bool IsBundleSpec() const {
98 return HasPrefix(filename_, ".cvmfsbundle-", false /* ignore_case */);
99 }
100 inline bool WasBundleSpec() const {
101 return HasPrefix(filename_, ".cvmfsbundle-", false /* ignore_case */);
102 }
103
104 inline unsigned int GetRdevMajor() const {
105 assert(IsSpecialFile());
106 StatUnion(true);
107 return major(union_stat_.stat.st_rdev);
108 }
109
110 inline unsigned int GetRdevMinor() const {
111 assert(IsSpecialFile());
112 StatUnion(true);
113 return minor(union_stat_.stat.st_rdev);
114 }
115
116 bool HasCatalogMarker() const { return has_catalog_marker_; }
117 bool HasGraftMarker() const { return graft_marker_present_; }
118 bool HasCompressionAlgorithm() const { return has_compression_algorithm_; }
119 bool IsValidGraft() const { return valid_graft_; }
120 bool IsChunkedGraft() const { return graft_chunklist_; }
121
122 inline const FileChunkList *GetGraftChunks() const {
123 return graft_chunklist_;
124 }
125 inline shash::Any GetContentHash() const { return content_hash_; }
126 inline void SetContentHash(const shash::Any &hash) { content_hash_ = hash; }
127 inline bool HasContentHash() const { return !content_hash_.IsNull(); }
128 void SetExternalData(bool val) { external_data_ = val; }
129 void SetDirectIo(bool val) { direct_io_ = val; }
130 void SetVolatile(bool val) { volatile_ = val; }
131
132 inline zlib::Algorithms GetCompressionAlgorithm() const {
133 return compression_algorithm_;
134 }
135 inline void SetCompressionAlgorithm(const zlib::Algorithms &alg) {
136 compression_algorithm_ = alg;
137 has_compression_algorithm_ = true;
138 }
139
140 /**
141 * Generates a DirectoryEntry that can be directly stored into a catalog db.
142 * Note: this sets the inode fields to kInvalidInode as well as the link
143 * count to 1 if MaskHardlink() has been called before (cf. OverlayFS)
144 *
145 * If nanosecond timestamps are off, the directory entry will have a
146 * default initialized, negative nanosecond timestamp and as a result
147 * the corresponding field in the catalog table will be NULL.
148 *
149 * @return a DirectoryEntry structure to be written into a catalog
150 */
151 virtual catalog::DirectoryEntryBase CreateBasicCatalogDirent(
152 bool enable_mtime_ns) const = 0;
153
154 140 inline std::string GetRelativePath() const {
155 140 return (relative_parent_path_.empty())
156 24 ? filename_
157 116 : relative_parent_path_
158
9/21
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 116 times.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 116 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 116 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 116 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 116 times.
✓ Branch 19 taken 24 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 140 times.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
280 + (filename_.empty() ? "" : ("/" + filename_));
159 }
160
161 std::string GetRdOnlyPath() const;
162 std::string GetUnionPath() const;
163 std::string GetScratchPath() const;
164
165 void MarkAsWhiteout(const std::string &actual_filename);
166 void MarkAsOpaqueDirectory();
167
168 /**
169 * Union file systems (i.e. OverlayFS) might not properly support hardlinks,
170 * forcing us to ignore them during publishing. A 'masked hardlink' will be
171 * treated as a normal file (linkcount == 1). Hence, any created hardlinks
172 * will be broken up into individual files with differing inodes.
173 */
174 inline void MaskHardlink() { masked_hardlink_ = true; }
175 inline bool HasHardlinks() const {
176 return !masked_hardlink_ && GetUnionLinkcount() > 1;
177 }
178
179 unsigned int GetRdOnlyLinkcount() const;
180 uint64_t GetRdOnlyInode() const;
181 unsigned int GetUnionLinkcount() const;
182 uint64_t GetUnionInode() const;
183 uint64_t GetScratchSize() const;
184 uint64_t GetRdOnlySize() const;
185
186 84 inline std::string filename() const { return filename_; }
187 inline std::string relative_parent_path() const {
188 return relative_parent_path_;
189 }
190
191 virtual IngestionSource *CreateIngestionSource() const = 0;
192 virtual void MakePlaceholderDirectory() const = 0;
193 void SetCatalogMarker() { has_catalog_marker_ = true; }
194
195 bool operator==(const SyncItem &other) const {
196 return ((relative_parent_path_ == other.relative_parent_path_)
197 && (filename_ == other.filename_));
198 }
199
200 protected:
201 /**
202 * create a new SyncItem
203 * Note: SyncItems cannot be created by any using code. SyncUnion will take
204 * care of their creating through a factory method to make sure they
205 * are initialised correctly (whiteout, hardlink handling, ...)
206 *
207 * @param dirPath the RELATIVE path to the file
208 * @param filename the name of the file ;-)
209 * @param entryType well...
210 */
211 SyncItem(const std::string &relative_parent_path,
212 const std::string &filename,
213 const SyncUnion *union_engine,
214 const SyncItemType entry_type);
215
216 inline platform_stat64 GetUnionStat() const {
217 StatUnion();
218 return union_stat_.stat;
219 }
220
221 SyncItemType GetRdOnlyFiletype() const;
222 SyncItemType GetUnionFiletype() const;
223
224 virtual SyncItemType GetScratchFiletype() const = 0;
225
226 /**
227 * Checks if the SyncItem _is_ the given file type (file, dir, symlink, ...)
228 * in the union file system volume. Hence: After the publish operation, the
229 * file will be this type in CVMFS.
230 * @param expected_type the file type to be checked against
231 * @return true if file type matches the expected type
232 */
233 virtual bool IsType(const SyncItemType expected_type) const = 0;
234
235 /**
236 * Checks if the SyncItem _was_ the given file type (file, dir, symlink, ...)
237 * in CVMFS (or the lower layer of the union file system). Hence: Before the
238 * current transaction the file _was_ this type in CVMFS.
239 * @param expected_type the file type to be checked against
240 * @return true if file type was the expected type in CVMFS
241 */
242 38 inline bool WasType(const SyncItemType expected_type) const {
243
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 4 times.
38 if (rdonly_type_ == kItemUnknown) {
244 34 rdonly_type_ = GetRdOnlyFiletype();
245 }
246 38 return rdonly_type_ == expected_type;
247 }
248
249 /**
250 * Structure to cache stat calls to the different file locations.
251 */
252 struct EntryStat {
253 156 EntryStat() : obtained(false), error_code(0) {
254 156 memset(&stat, 0, sizeof(stat));
255 156 }
256
257 inline SyncItemType GetSyncItemType() const {
258 assert(obtained);
259 if (S_ISREG(stat.st_mode))
260 return kItemFile;
261 if (S_ISLNK(stat.st_mode))
262 return kItemSymlink;
263 if (S_ISDIR(stat.st_mode))
264 return kItemDir;
265 if (S_ISFIFO(stat.st_mode))
266 return kItemFifo;
267 if (S_ISSOCK(stat.st_mode))
268 return kItemSocket;
269 if (S_ISCHR(stat.st_mode))
270 return kItemCharacterDevice;
271 if (S_ISBLK(stat.st_mode))
272 return kItemBlockDevice;
273 return kItemUnknown;
274 }
275
276 bool obtained; /**< false at the beginning, true after first stat call */
277 int error_code; /**< errno value of the stat call */
278 platform_stat64 stat;
279 };
280
281 static void StatGeneric(const std::string &path,
282 EntryStat *info,
283 const bool refresh);
284 SyncItemType GetGenericFiletype(const EntryStat &stat) const;
285 void CheckMarkerFiles();
286
287 mutable SyncItemType rdonly_type_;
288 mutable EntryStat scratch_stat_;
289
290 ssize_t graft_size_;
291
292 // The hash of regular file's content
293 shash::Any content_hash_;
294
295 mutable SyncItemType scratch_type_;
296
297 private:
298 void CheckCatalogMarker();
299
300 std::string filename_;
301
302 std::string GetGraftMarkerPath() const;
303 void CheckGraft();
304
305 const SyncUnion *union_engine_; /**< this SyncUnion created this object */
306
307 mutable EntryStat rdonly_stat_;
308 mutable EntryStat union_stat_;
309
310 bool whiteout_; /**< SyncUnion marked this as whiteout */
311 bool opaque_; /**< SyncUnion marked this as opaque dir*/
312 bool masked_hardlink_; /**< SyncUnion masked out the linkcount */
313 bool has_catalog_marker_; /**< directory containing .cvmfscatalog */
314 bool valid_graft_; /**< checksum and size in graft marker */
315 bool graft_marker_present_; /**< .cvmfsgraft-$filename exists */
316
317 bool external_data_;
318 bool direct_io_;
319 bool volatile_;
320 std::string relative_parent_path_;
321
322 /**
323 * Chunklist from graft. Not initialized by default to save memory.
324 */
325 FileChunkList *graft_chunklist_;
326
327 // The compression algorithm for the file
328 zlib::Algorithms compression_algorithm_;
329 // The compression algorithm has been set explicitly
330 bool has_compression_algorithm_;
331
332 // Lazy evaluation and caching of results of file stats
333 34 inline void StatRdOnly(const bool refresh = false) const {
334 34 StatGeneric(GetRdOnlyPath(), &rdonly_stat_, refresh);
335 34 }
336 inline void StatUnion(const bool refresh = false) const {
337 StatGeneric(GetUnionPath(), &union_stat_, refresh);
338 }
339 virtual void StatScratch(const bool refresh) const = 0;
340 };
341
342 typedef std::map<std::string, SharedPtr<SyncItem> > SyncItemList;
343
344 class SyncItemNative : public SyncItem {
345 friend class SyncUnion;
346 virtual catalog::DirectoryEntryBase CreateBasicCatalogDirent(
347 bool enable_mtime_ns) const;
348 virtual IngestionSource *CreateIngestionSource() const;
349 virtual void MakePlaceholderDirectory() const { assert(false); }
350 virtual SyncItemType GetScratchFiletype() const;
351 virtual bool IsType(const SyncItemType expected_type) const;
352 virtual void StatScratch(const bool refresh) const {
353 StatGeneric(GetScratchPath(), &scratch_stat_, refresh);
354 }
355
356 protected:
357 18 SyncItemNative(const std::string &relative_parent_path,
358 const std::string &filename, const SyncUnion *union_engine,
359 const SyncItemType entry_type)
360 18 : SyncItem(relative_parent_path, filename, union_engine, entry_type) {
361
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 CheckMarkerFiles();
362 18 }
363 };
364
365 } // namespace publish
366
367 #endif // CVMFS_SYNC_ITEM_H_
368