GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/directory_entry.h
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 121 131 92.4%
Branches: 21 36 58.3%

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.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 */
82 struct Difference {
83 static const unsigned int kIdentical = 0x000;
84 static const unsigned int kName = 0x001;
85 static const unsigned int kLinkcount = 0x002;
86 static const unsigned int kSize = 0x004;
87 static const unsigned int kMode = 0x008;
88 static const unsigned int kMtime = 0x010;
89 static const unsigned int kSymlink = 0x020;
90 static const unsigned int kChecksum = 0x040;
91 static const unsigned int kHardlinkGroup = 0x080;
92 static const unsigned int kNestedCatalogTransitionFlags = 0x100;
93 static const unsigned int kChunkedFileFlag = 0x200;
94 static const unsigned int kHasXattrsFlag = 0x400;
95 static const unsigned int kExternalFileFlag = 0x800;
96 static const unsigned int kBindMountpointFlag = 0x1000;
97 static const unsigned int kHiddenFlag = 0x2000;
98 static const unsigned int kDirectIoFlag = 0x4000;
99 };
100 typedef unsigned int Differences;
101
102 /**
103 * Zero-constructed DirectoryEntry objects are unusable as such.
104 */
105 1285165 inline DirectoryEntryBase()
106 1285165 : inode_(kInvalidInode)
107 1285165 , mode_(0)
108 1285165 , uid_(0)
109 1285165 , gid_(0)
110 1285165 , size_(0)
111 1285165 , mtime_(0)
112 1285165 , linkcount_(1) // generally a normal file has linkcount 1 -> default
113 1285165 , has_xattrs_(false)
114 1285165 , is_external_file_(false)
115 1285165 , is_direct_io_(false)
116
1/2
✓ Branch 3 taken 1285165 times.
✗ Branch 4 not taken.
1285165 , compression_algorithm_(zlib::kZlibDefault)
117 1285165 { }
118
119 1175 inline bool IsRegular() const { return S_ISREG(mode_); }
120 1348 inline bool IsLink() const { return S_ISLNK(mode_); }
121 1332 inline bool IsDirectory() const { return S_ISDIR(mode_); }
122 502 inline bool IsFifo() const { return S_ISFIFO(mode_); }
123 498 inline bool IsSocket() const { return S_ISSOCK(mode_); }
124 1207 inline bool IsCharDev() const { return S_ISCHR(mode_); }
125 1207 inline bool IsBlockDev() const { return S_ISBLK(mode_); }
126 502 inline bool IsSpecial() const {
127
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();
128 }
129 732 inline bool IsExternalFile() const { return is_external_file_; }
130 341 inline bool IsDirectIo() const { return is_direct_io_; }
131 856 inline bool HasXattrs() const { return has_xattrs_; }
132
133 367 inline inode_t inode() const { return inode_; }
134 253 inline uint32_t linkcount() const { return linkcount_; }
135 1533 inline NameString name() const { return name_; }
136 58 inline LinkString symlink() const { return symlink_; }
137 46 inline time_t mtime() const { return mtime_; }
138 46 inline unsigned int mode() const { return mode_; }
139 39 inline uid_t uid() const { return uid_; }
140 39 inline gid_t gid() const { return gid_; }
141 197 inline shash::Any checksum() const { return checksum_; }
142 986 inline const shash::Any *checksum_ptr() const { return &checksum_; }
143 inline shash::Algorithms hash_algorithm() const {
144 return checksum_.algorithm;
145 }
146 681 inline uint64_t size() const {
147
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 673 times.
681 if (IsLink())
148 8 return symlink().GetLength();
149
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())
150 return 0;
151 673 return size_;
152 }
153 36 inline dev_t rdev() const {
154
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())
155 return size_;
156 36 return 1;
157 }
158 513 inline std::string GetFullPath(const std::string &parent_directory) const {
159 513 std::string file_path = parent_directory + "/";
160
3/6
✓ Branch 1 taken 513 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 513 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 513 times.
✗ Branch 10 not taken.
513 file_path.append(name().GetChars(), name().GetLength());
161 513 return file_path;
162 }
163
164 36 inline void set_inode(const inode_t inode) { inode_ = inode; }
165 232 inline void set_linkcount(const uint32_t linkcount) {
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 232 times.
232 assert(linkcount > 0);
167 232 linkcount_ = linkcount;
168 232 }
169 inline void set_symlink(const LinkString &symlink) {
170 symlink_ = symlink;
171 }
172 461 inline void set_has_xattrs(const bool has_xattrs) {
173 461 has_xattrs_ = has_xattrs;
174 461 }
175
176 297 inline zlib::Algorithms compression_algorithm() const {
177 297 return compression_algorithm_;
178 }
179
180 /**
181 * Converts to a stat struct as required by many Fuse callbacks.
182 * @return the struct stat for this DirectoryEntry
183 */
184 34 inline struct stat GetStatStructure() const {
185 struct stat s;
186 34 memset(&s, 0, sizeof(s));
187 34 s.st_dev = 1;
188 34 s.st_ino = inode_;
189 34 s.st_mode = mode_;
190 34 s.st_nlink = linkcount();
191 34 s.st_uid = uid();
192 34 s.st_gid = gid();
193 34 s.st_rdev = rdev();
194 34 s.st_size = static_cast<off_t>(size());
195 34 s.st_blksize = 4096; // will be ignored by Fuse
196 34 s.st_blocks = static_cast<blkcnt_t>(1 + size() / 512);
197 34 s.st_atime = mtime_;
198 34 s.st_mtime = mtime_;
199 34 s.st_ctime = mtime_;
200 34 return s;
201 }
202
203 Differences CompareTo(const DirectoryEntryBase &other) const;
204 inline bool operator ==(const DirectoryEntryBase &other) const {
205 return CompareTo(other) == Difference::kIdentical;
206 }
207 inline bool operator !=(const DirectoryEntryBase &other) const {
208 return !(*this == other);
209 }
210
211 protected:
212 // Inodes are generated based on the rowid of the entry in the file catalog.
213 inode_t inode_;
214
215 // Data from struct stat
216 NameString name_;
217 unsigned int mode_;
218 uid_t uid_;
219 gid_t gid_;
220 uint64_t size_;
221 time_t mtime_;
222 LinkString symlink_;
223 uint32_t linkcount_;
224 // In order to save memory, we only indicate if a directory entry has custom
225 // extended attributes. Another call to the file catalog is necessary to
226 // get them.
227 bool has_xattrs_;
228
229 // The cryptographic hash is not part of the file system intrinsics, though
230 // it can be computed just using the file contents. We therefore put it in
231 // this base class.
232 shash::Any checksum_;
233
234 bool is_external_file_;
235 bool is_direct_io_;
236
237 // The compression algorithm
238 zlib::Algorithms compression_algorithm_;
239 };
240
241
242 /**
243 * In addition to the file system meta-data covered by DirectoryEntryBase,
244 * DirectoryEntries contain cvmfs-specific meta data. Currently these are the
245 * following things:
246 * - Pointer to the originating catalog
247 * - Markers for nested catalog transition points (mountpoint and root entry)
248 * - Transient marker storing the time of caching (Fuse page caches).
249 * This is required to invalidate caches after a catalog update
250 * - Hardlink group used to emulate hardlinks in cvmfs
251 */
252 class DirectoryEntry : public DirectoryEntryBase {
253 // Simplify creation of DirectoryEntry objects
254 friend class SqlLookup;
255 // Simplify write of DirectoryEntry objects in database
256 friend class SqlDirentWrite;
257 // For fixing DirectoryEntry glitches
258 friend class swissknife::CommandMigrate;
259 // TODO(rmeusel): remove this dependency
260 friend class WritableCatalogManager;
261 // Create DirectoryEntries for unit test purposes.
262 friend class DirectoryEntryTestFactory;
263
264 public:
265 /**
266 * This is _kind of_ a copy constructor allowing us to create
267 * DirectoryEntries directly from DirectoryEntryBase objects. We make it
268 * explicit, to disallow black magic from happening. It uses the copy
269 * constructor of DirectoryEntryBase and initializes the additional fields of
270 * DirectoryEntry.
271 */
272 245 inline explicit DirectoryEntry(const DirectoryEntryBase& base)
273 245 : DirectoryEntryBase(base)
274 245 , hardlink_group_(0)
275 245 , is_nested_catalog_root_(false)
276 245 , is_nested_catalog_mountpoint_(false)
277 245 , is_bind_mountpoint_(false)
278 245 , is_chunked_file_(false)
279 245 , is_hidden_(false)
280 245 , is_negative_(false) { }
281
282 1285063 inline DirectoryEntry()
283 2570126 : hardlink_group_(0)
284 1285063 , is_nested_catalog_root_(false)
285 1285063 , is_nested_catalog_mountpoint_(false)
286 1285063 , is_bind_mountpoint_(false)
287 1285063 , is_chunked_file_(false)
288 1285063 , is_hidden_(false)
289 1285063 , is_negative_(false) { }
290
291 100 inline explicit DirectoryEntry(SpecialDirents special_type)
292 200 : hardlink_group_(0)
293 100 , is_nested_catalog_root_(false)
294 100 , is_nested_catalog_mountpoint_(false)
295 100 , is_bind_mountpoint_(false)
296 100 , is_chunked_file_(false)
297 100 , is_hidden_(false)
298
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
100 , is_negative_(true) { assert(special_type == kDirentNegative); }
299
300 14 inline SpecialDirents GetSpecial() const {
301
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 return is_negative_ ? kDirentNegative : kDirentNormal;
302 }
303
304 Differences CompareTo(const DirectoryEntry &other) const;
305 inline bool operator ==(const DirectoryEntry &other) const {
306 return CompareTo(other) == Difference::kIdentical;
307 }
308 inline bool operator !=(const DirectoryEntry &other) const {
309 return !(*this == other);
310 }
311
312 92 inline bool IsNegative() const { return is_negative_; }
313 1075 inline bool IsNestedCatalogRoot() const { return is_nested_catalog_root_; }
314 928 inline bool IsNestedCatalogMountpoint() const {
315 928 return is_nested_catalog_mountpoint_;
316 }
317 695 inline bool IsBindMountpoint() const { return is_bind_mountpoint_; }
318 1522 inline bool IsChunkedFile() const { return is_chunked_file_; }
319 821 inline bool IsHidden() const { return is_hidden_; }
320 44 inline uint32_t hardlink_group() const { return hardlink_group_; }
321
322 10 inline void set_hardlink_group(const uint32_t group) {
323 10 hardlink_group_ = group;
324 10 }
325 44 inline void set_is_nested_catalog_mountpoint(const bool val) {
326 44 is_nested_catalog_mountpoint_ = val;
327 44 }
328 43 inline void set_is_nested_catalog_root(const bool val) {
329 43 is_nested_catalog_root_ = val;
330 43 }
331 inline void set_is_bind_mountpoint(const bool val) {
332 is_bind_mountpoint_ = val;
333 }
334 100 inline void set_is_chunked_file(const bool val) {
335 100 is_chunked_file_ = val;
336 100 }
337 inline void set_is_hidden(const bool val) {
338 is_hidden_ = val;
339 }
340
341 private:
342 /**
343 * Hardlink handling is emulated in CVMFS. Since inodes are allocated on
344 * demand we save hardlink relationships using the same hardlink_group.
345 */
346 uint32_t hardlink_group_;
347
348 // TODO(jblomer): transform into bitfield to save memory
349 bool is_nested_catalog_root_;
350 bool is_nested_catalog_mountpoint_;
351 bool is_bind_mountpoint_;
352 bool is_chunked_file_;
353 bool is_hidden_;
354 bool is_negative_;
355 };
356
357
358 /**
359 * Saves memory for large directory listings.
360 */
361 struct StatEntry {
362 NameString name;
363 struct stat info;
364
365 5 StatEntry() { memset(&info, 0, sizeof(info)); }
366 StatEntry(const NameString &n, const struct stat &i) : name(n), info(i) { }
367 };
368
369
370 typedef std::vector<DirectoryEntry> DirectoryEntryList;
371 typedef std::vector<DirectoryEntryBase> DirectoryEntryBaseList;
372 // TODO(jblomer): use mmap for large listings
373 typedef BigVector<StatEntry> StatEntryList;
374
375 } // namespace catalog
376
377 #endif // CVMFS_DIRECTORY_ENTRY_H_
378