GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/directory_entry.h
Date: 2025-02-09 02:34:19
Exec Total Coverage
Lines: 125 138 90.6%
Branches: 22 38 57.9%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Data wrappers for single dentries. In addition to the normal file meta data
5 * it manages bookkeeping data specific to cvmfs such as the associated catalog.
6 */
7
8 #ifndef CVMFS_DIRECTORY_ENTRY_H_
9 #define CVMFS_DIRECTORY_ENTRY_H_
10
11 #include <sys/types.h>
12
13 #include <cassert>
14 #include <cstring>
15 #include <string>
16 #include <vector>
17
18 #include "bigvector.h"
19 #include "compression/compression.h"
20 #include "crypto/hash.h"
21 #include "shortstring.h"
22 #include "util/platform.h"
23
24 namespace publish {
25 class SyncItem;
26 class SyncItemNative;
27 class SyncItemTar;
28 class SyncItemDummyDir;
29 class SyncItemDummyCatalog;
30 }
31 namespace swissknife {
32 class CommandMigrate;
33 }
34
35 namespace catalog {
36
37 // Create DirectoryEntries for unit test purposes.
38 class DirectoryEntryTestFactory;
39
40 class MockCatalogManager;
41 class Catalog;
42 class WritableCatalogManager;
43
44 template <class CatalogMgrT>
45 class CatalogBalancer;
46 typedef uint64_t inode_t;
47
48 enum SpecialDirents {
49 kDirentNormal = 0,
50 kDirentNegative,
51 };
52
53 /**
54 * Wrapper around struct dirent. Only contains file system related meta data
55 * for a directory entry.
56 * TODO(jblomer): separation to DirectoryEntry not quite clear: this one also
57 * contains hash, compression algorithm and external flag
58 */
59 class DirectoryEntryBase {
60 // For testing the catalog balancing
61 friend class CatalogBalancer<MockCatalogManager>;
62 // Create .cvmfscatalog and .cvmfsautocatalog files
63 friend class CatalogBalancer<WritableCatalogManager>;
64 // Simplify creation of DirectoryEntry objects for write back
65 friend class publish::SyncItem;
66 friend class publish::SyncItemNative;
67 friend class publish::SyncItemTar;
68 friend class publish::SyncItemDummyDir;
69 friend class publish::SyncItemDummyCatalog;
70 // Simplify file system like _touch_ of DirectoryEntry objects
71 friend class SqlDirentTouch;
72 // Allow creation of virtual directories and files
73 friend class VirtualCatalog;
74
75 public:
76 static const inode_t kInvalidInode = 0;
77
78 /**
79 * Used in the swissknife for sanity checks and catalog migration. If
80 * anything is added, also adjust PrintDifferences in swissknife::CommandDiff
81 * and CommandCheck::CompareEntries
82 */
83 struct Difference {
84 static const unsigned int kIdentical = 0x00000;
85 static const unsigned int kName = 0x00001;
86 static const unsigned int kLinkcount = 0x00002;
87 static const unsigned int kSize = 0x00004;
88 static const unsigned int kMode = 0x00008;
89 static const unsigned int kMtime = 0x00010;
90 static const unsigned int kSymlink = 0x00020;
91 static const unsigned int kChecksum = 0x00040;
92 static const unsigned int kHardlinkGroup = 0x00080;
93 static const unsigned int kNestedCatalogTransitionFlags = 0x00100;
94 static const unsigned int kChunkedFileFlag = 0x00200;
95 static const unsigned int kHasXattrsFlag = 0x00400;
96 static const unsigned int kExternalFileFlag = 0x00800;
97 static const unsigned int kBindMountpointFlag = 0x01000;
98 static const unsigned int kHiddenFlag = 0x02000;
99 static const unsigned int kDirectIoFlag = 0x04000;
100 static const unsigned int kUid = 0x08000;
101 static const unsigned int kGid = 0x10000;
102 };
103 typedef unsigned int Differences;
104
105 /**
106 * Zero-constructed DirectoryEntry objects are unusable as such.
107 */
108 1256496 inline DirectoryEntryBase()
109 1256496 : inode_(kInvalidInode)
110 1256496 , mode_(0)
111 1256496 , uid_(0)
112 1256496 , gid_(0)
113 1256496 , size_(0)
114 1256496 , mtime_(0)
115 1256496 , mtime_ns_(-1)
116 1256496 , linkcount_(1) // generally a normal file has linkcount 1 -> default
117 1256496 , has_xattrs_(false)
118 1256496 , is_external_file_(false)
119 1256496 , is_direct_io_(false)
120
1/2
✓ Branch 3 taken 1256496 times.
✗ Branch 4 not taken.
1256496 , compression_algorithm_(zlib::kZlibDefault)
121 1256496 { }
122
123 1175 inline bool IsRegular() const { return S_ISREG(mode_); }
124 1348 inline bool IsLink() const { return S_ISLNK(mode_); }
125 1338 inline bool IsDirectory() const { return S_ISDIR(mode_); }
126 502 inline bool IsFifo() const { return S_ISFIFO(mode_); }
127 498 inline bool IsSocket() const { return S_ISSOCK(mode_); }
128 1207 inline bool IsCharDev() const { return S_ISCHR(mode_); }
129 1207 inline bool IsBlockDev() const { return S_ISBLK(mode_); }
130 502 inline bool IsSpecial() const {
131
5/8
✓ Branch 1 taken 498 times.
✓ Branch 2 taken 4 times.
✓ Branch 4 taken 498 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 498 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 498 times.
502 return IsFifo() || IsSocket() || IsCharDev() || IsBlockDev();
132 }
133 732 inline bool IsExternalFile() const { return is_external_file_; }
134 341 inline bool IsDirectIo() const { return is_direct_io_; }
135 859 inline bool HasXattrs() const { return has_xattrs_; }
136 747 inline bool HasMtimeNs() const { return mtime_ns_ >= 0; }
137
138 367 inline inode_t inode() const { return inode_; }
139 253 inline uint32_t linkcount() const { return linkcount_; }
140 1547 inline NameString name() const { return name_; }
141 58 inline LinkString symlink() const { return symlink_; }
142 46 inline time_t mtime() const { return mtime_; }
143 44 inline int32_t mtime_ns() const { return mtime_ns_; }
144 46 inline unsigned int mode() const { return mode_; }
145 83 inline uid_t uid() const { return uid_; }
146 83 inline gid_t gid() const { return gid_; }
147 197 inline shash::Any checksum() const { return checksum_; }
148 986 inline const shash::Any *checksum_ptr() const { return &checksum_; }
149 inline shash::Algorithms hash_algorithm() const {
150 return checksum_.algorithm;
151 }
152 681 inline uint64_t size() const {
153
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 673 times.
681 if (IsLink())
154 8 return symlink().GetLength();
155
3/6
✓ Branch 1 taken 673 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 673 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 673 times.
673 if (IsBlockDev() || IsCharDev())
156 return 0;
157 673 return size_;
158 }
159 36 inline dev_t rdev() const {
160
3/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 36 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 if (IsBlockDev() || IsCharDev())
161 return size_;
162 36 return 1;
163 }
164 519 inline std::string GetFullPath(const std::string &parent_directory) const {
165 519 std::string file_path = parent_directory + "/";
166
3/6
✓ Branch 1 taken 519 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 519 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 519 times.
✗ Branch 10 not taken.
519 file_path.append(name().GetChars(), name().GetLength());
167 519 return file_path;
168 }
169
170 36 inline void set_inode(const inode_t inode) { inode_ = inode; }
171 232 inline void set_linkcount(const uint32_t linkcount) {
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 assert(linkcount > 0);
173 232 linkcount_ = linkcount;
174 232 }
175 inline void set_symlink(const LinkString &symlink) {
176 symlink_ = symlink;
177 }
178 461 inline void set_has_xattrs(const bool has_xattrs) {
179 461 has_xattrs_ = has_xattrs;
180 461 }
181
182 297 inline zlib::Algorithms compression_algorithm() const {
183 297 return compression_algorithm_;
184 }
185
186 /**
187 * Converts to a stat struct as required by many Fuse callbacks.
188 * @return the struct stat for this DirectoryEntry
189 */
190 34 inline struct stat GetStatStructure() const {
191 struct stat s;
192 34 memset(&s, 0, sizeof(s));
193 34 s.st_dev = 1;
194 34 s.st_ino = inode_;
195 34 s.st_mode = mode_;
196 34 s.st_nlink = linkcount();
197 34 s.st_uid = uid();
198 34 s.st_gid = gid();
199 34 s.st_rdev = rdev();
200 34 s.st_size = static_cast<off_t>(size());
201 34 s.st_blksize = 4096; // will be ignored by Fuse
202 34 s.st_blocks = static_cast<blkcnt_t>(1 + size() / 512);
203 34 s.st_atime = mtime_;
204 34 s.st_mtime = mtime_;
205 34 s.st_ctime = mtime_;
206
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
34 if (HasMtimeNs()) {
207 #ifdef __APPLE__
208 s.st_atimespec.tv_nsec = mtime_ns_;
209 s.st_mtimespec.tv_nsec = mtime_ns_;
210 s.st_ctimespec.tv_nsec = mtime_ns_;
211 #else
212 s.st_atim.tv_nsec = mtime_ns_;
213 s.st_mtim.tv_nsec = mtime_ns_;
214 s.st_ctim.tv_nsec = mtime_ns_;
215 #endif
216 }
217 34 return s;
218 }
219
220 Differences CompareTo(const DirectoryEntryBase &other) const;
221 inline bool operator ==(const DirectoryEntryBase &other) const {
222 return CompareTo(other) == Difference::kIdentical;
223 }
224 inline bool operator !=(const DirectoryEntryBase &other) const {
225 return !(*this == other);
226 }
227
228 protected:
229 // Inodes are generated based on the rowid of the entry in the file catalog.
230 inode_t inode_;
231
232 // Data from struct stat
233 NameString name_;
234 unsigned int mode_;
235 uid_t uid_;
236 gid_t gid_;
237 uint64_t size_;
238 time_t mtime_;
239 // nanosecond part of the mtime. Only valid if non-negative
240 int32_t mtime_ns_;
241 LinkString symlink_;
242 uint32_t linkcount_;
243 // In order to save memory, we only indicate if a directory entry has custom
244 // extended attributes. Another call to the file catalog is necessary to
245 // get them.
246 bool has_xattrs_;
247
248 // The cryptographic hash is not part of the file system intrinsics, though
249 // it can be computed just using the file contents. We therefore put it in
250 // this base class.
251 shash::Any checksum_;
252
253 bool is_external_file_;
254 bool is_direct_io_;
255
256 // The compression algorithm
257 zlib::Algorithms compression_algorithm_;
258 };
259
260
261 /**
262 * In addition to the file system meta-data covered by DirectoryEntryBase,
263 * DirectoryEntries contain cvmfs-specific meta data. Currently these are the
264 * following things:
265 * - Pointer to the originating catalog
266 * - Markers for nested catalog transition points (mountpoint and root entry)
267 * - Transient marker storing the time of caching (Fuse page caches).
268 * This is required to invalidate caches after a catalog update
269 * - Hardlink group used to emulate hardlinks in cvmfs
270 */
271 class DirectoryEntry : public DirectoryEntryBase {
272 // Simplify creation of DirectoryEntry objects
273 friend class SqlLookup;
274 // Simplify write of DirectoryEntry objects in database
275 friend class SqlDirentWrite;
276 // For fixing DirectoryEntry glitches
277 friend class swissknife::CommandMigrate;
278 // TODO(rmeusel): remove this dependency
279 friend class WritableCatalogManager;
280 // Create DirectoryEntries for unit test purposes.
281 friend class DirectoryEntryTestFactory;
282
283 public:
284 /**
285 * This is _kind of_ a copy constructor allowing us to create
286 * DirectoryEntries directly from DirectoryEntryBase objects. We make it
287 * explicit, to disallow black magic from happening. It uses the copy
288 * constructor of DirectoryEntryBase and initializes the additional fields of
289 * DirectoryEntry.
290 */
291 245 inline explicit DirectoryEntry(const DirectoryEntryBase& base)
292 245 : DirectoryEntryBase(base)
293 245 , hardlink_group_(0)
294 245 , is_nested_catalog_root_(false)
295 245 , is_nested_catalog_mountpoint_(false)
296 245 , is_bind_mountpoint_(false)
297 245 , is_chunked_file_(false)
298 245 , is_hidden_(false)
299 245 , is_negative_(false) { }
300
301 1256394 inline DirectoryEntry()
302 2512788 : hardlink_group_(0)
303 1256394 , is_nested_catalog_root_(false)
304 1256394 , is_nested_catalog_mountpoint_(false)
305 1256394 , is_bind_mountpoint_(false)
306 1256394 , is_chunked_file_(false)
307 1256394 , is_hidden_(false)
308 1256394 , is_negative_(false) { }
309
310 100 inline explicit DirectoryEntry(SpecialDirents special_type)
311 200 : hardlink_group_(0)
312 100 , is_nested_catalog_root_(false)
313 100 , is_nested_catalog_mountpoint_(false)
314 100 , is_bind_mountpoint_(false)
315 100 , is_chunked_file_(false)
316 100 , is_hidden_(false)
317
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
100 , is_negative_(true) { assert(special_type == kDirentNegative); }
318
319 14 inline SpecialDirents GetSpecial() const {
320
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 return is_negative_ ? kDirentNegative : kDirentNormal;
321 }
322
323 Differences CompareTo(const DirectoryEntry &other) const;
324 inline bool operator ==(const DirectoryEntry &other) const {
325 return CompareTo(other) == Difference::kIdentical;
326 }
327 inline bool operator !=(const DirectoryEntry &other) const {
328 return !(*this == other);
329 }
330
331 92 inline bool IsNegative() const { return is_negative_; }
332 1075 inline bool IsNestedCatalogRoot() const { return is_nested_catalog_root_; }
333 928 inline bool IsNestedCatalogMountpoint() const {
334 928 return is_nested_catalog_mountpoint_;
335 }
336 695 inline bool IsBindMountpoint() const { return is_bind_mountpoint_; }
337 1522 inline bool IsChunkedFile() const { return is_chunked_file_; }
338 821 inline bool IsHidden() const { return is_hidden_; }
339 44 inline uint32_t hardlink_group() const { return hardlink_group_; }
340
341 10 inline void set_hardlink_group(const uint32_t group) {
342 10 hardlink_group_ = group;
343 10 }
344 44 inline void set_is_nested_catalog_mountpoint(const bool val) {
345 44 is_nested_catalog_mountpoint_ = val;
346 44 }
347 43 inline void set_is_nested_catalog_root(const bool val) {
348 43 is_nested_catalog_root_ = val;
349 43 }
350 inline void set_is_bind_mountpoint(const bool val) {
351 is_bind_mountpoint_ = val;
352 }
353 100 inline void set_is_chunked_file(const bool val) {
354 100 is_chunked_file_ = val;
355 100 }
356 inline void set_is_hidden(const bool val) {
357 is_hidden_ = val;
358 }
359
360 private:
361 /**
362 * Hardlink handling is emulated in CVMFS. Since inodes are allocated on
363 * demand we save hardlink relationships using the same hardlink_group.
364 */
365 uint32_t hardlink_group_;
366
367 // TODO(jblomer): transform into bitfield to save memory
368 bool is_nested_catalog_root_;
369 bool is_nested_catalog_mountpoint_;
370 bool is_bind_mountpoint_;
371 bool is_chunked_file_;
372 bool is_hidden_;
373 bool is_negative_;
374 };
375
376
377 /**
378 * Saves memory for large directory listings.
379 */
380 struct StatEntry {
381 NameString name;
382 struct stat info;
383
384 5 StatEntry() { memset(&info, 0, sizeof(info)); }
385 StatEntry(const NameString &n, const struct stat &i) : name(n), info(i) { }
386 };
387
388
389 typedef std::vector<DirectoryEntry> DirectoryEntryList;
390 typedef std::vector<DirectoryEntryBase> DirectoryEntryBaseList;
391 // TODO(jblomer): use mmap for large listings
392 typedef BigVector<StatEntry> StatEntryList;
393
394 } // namespace catalog
395
396 #endif // CVMFS_DIRECTORY_ENTRY_H_
397