Directory: | cvmfs/ |
---|---|
File: | cvmfs/magic_xattr.h |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 36 | 37 | 97.3% |
Branches: | 4 | 8 | 50.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #ifndef CVMFS_MAGIC_XATTR_H_ | ||
6 | #define CVMFS_MAGIC_XATTR_H_ | ||
7 | |||
8 | #include <map> | ||
9 | #include <set> | ||
10 | #include <string> | ||
11 | #include <utility> | ||
12 | #include <vector> | ||
13 | |||
14 | #include "backoff.h" | ||
15 | #include "catalog_counters.h" | ||
16 | #include "directory_entry.h" | ||
17 | #include "shortstring.h" | ||
18 | #include "util/exception.h" | ||
19 | #include "util/string.h" | ||
20 | |||
21 | class MountPoint; | ||
22 | |||
23 | enum MagicXattrFlavor { | ||
24 | kXattrBase = 0, | ||
25 | kXattrWithHash, | ||
26 | kXattrRegular, | ||
27 | kXattrExternal, | ||
28 | kXattrSymlink, | ||
29 | kXattrAuthz | ||
30 | }; | ||
31 | |||
32 | enum MagicXattrMode { | ||
33 | kXattrMachineMode = 0, | ||
34 | kXattrHumanMode | ||
35 | }; | ||
36 | |||
37 | class MagicXattrManager; // needed for BaseMagicXattr | ||
38 | /** | ||
39 | * This is a base class for magic extended attribute. Every extended | ||
40 | * attributes must inherit from this class. It should be generally used only | ||
41 | * in cooperation with MagicXattrManager. | ||
42 | * It contains an access mutex. Users should use Lock() and Release() | ||
43 | * before and after usage (Lock() is called implicitly in MagicXattrManager). | ||
44 | * | ||
45 | * To read out the attribute value, do: | ||
46 | * 0. Get an instance through MagicXattrManager::Get() | ||
47 | * 1. Call PrepareValueFenced() inside FuseRemounter::fence() | ||
48 | * 2. Call GetValue(int32_t requested_page, const MagicXattrMode mode); | ||
49 | * It returns a <bool, string> pair where the bool is set to true if the | ||
50 | * request was successful and the string contains the actual value. | ||
51 | * GetValue() can be called outside the fence. | ||
52 | * This will internally call FinalizeValue() to finalize the value | ||
53 | * preparation outside the fuse fence. | ||
54 | * | ||
55 | * Implementation notes: | ||
56 | * - Each MagicXattr must implement it's own FinalizeValue() function which | ||
57 | * has to write its value into result_pages_. | ||
58 | * - result_pages_ is a vector of strings. Each string is <= kMaxCharsPerPage | ||
59 | * - BaseMagicXattr::GetValue() takes care of clearing result_pages_ before each | ||
60 | * new call of FinalizeValue(), and takes care of adding a header for the | ||
61 | * human readable version | ||
62 | * - In case an invalid request happens, GetValue() <bool> is set to false. | ||
63 | * "ENOENT" will be returned, while in human-readable mode a more verbose | ||
64 | * error message will be returned | ||
65 | */ | ||
66 | class BaseMagicXattr { | ||
67 | friend class MagicXattrManager; | ||
68 | FRIEND_TEST(T_MagicXattr, ProtectedXattr); | ||
69 | FRIEND_TEST(T_MagicXattr, TestFqrn); | ||
70 | FRIEND_TEST(T_MagicXattr, TestLogBuffer); | ||
71 | |||
72 | public: | ||
73 | 46890 | BaseMagicXattr() : is_protected_(false) { | |
74 | 46890 | const int retval = pthread_mutex_init(&access_mutex_, NULL); | |
75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46890 times.
|
46890 | assert(retval == 0); |
76 | 46890 | } | |
77 | |||
78 | /** | ||
79 | * Mark a Xattr protected so that only certain users with the correct gid | ||
80 | * can access it. | ||
81 | */ | ||
82 | 9 | void MarkProtected() { is_protected_ = true; } | |
83 | |||
84 | |||
85 | // TODO(hereThereBeDragons) from C++11 should be marked final | ||
86 | /** | ||
87 | * Access right check before normal fence | ||
88 | */ | ||
89 | bool PrepareValueFencedProtected(gid_t gid); | ||
90 | |||
91 | /** | ||
92 | * This function needs to be called after PrepareValueFenced(), | ||
93 | * which prepares the necessary data and header for kXattrHumanMode. | ||
94 | * It does the computationaly intensive part, which should not | ||
95 | * be done inside the FuseRemounter::fence(), and returns the | ||
96 | * value. | ||
97 | * | ||
98 | * Internally it calls FinalizeValue() which each MagicXAttr has to implement | ||
99 | * to set the value of result_pages_ | ||
100 | * | ||
101 | * @params | ||
102 | * - requested_page: | ||
103 | * >= 0: requested paged of the attribute | ||
104 | * -1: get info about xattr: e.g. the number of pages available | ||
105 | * - mode: either machine-readable or human-readable | ||
106 | * | ||
107 | * @returns | ||
108 | * std::pair<bool, std::string> | ||
109 | * bool = false if in machine-readable mode an invalid request is performed | ||
110 | * (human-readable mode always succeeds and gives a verbose message) | ||
111 | * otherwise true | ||
112 | * std::string = the actual value of the attribute or info element | ||
113 | * ( or error message in human-readable mode) | ||
114 | * | ||
115 | */ | ||
116 | std::pair<bool, std::string> GetValue(int32_t requested_page, | ||
117 | const MagicXattrMode mode); | ||
118 | |||
119 | 279 | virtual MagicXattrFlavor GetXattrFlavor() { return kXattrBase; } | |
120 | |||
121 | 36 | void Lock(PathString path, catalog::DirectoryEntry *dirent) { | |
122 | 36 | const int retval = pthread_mutex_lock(&access_mutex_); | |
123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | assert(retval == 0); |
124 | 36 | path_ = path; | |
125 | 36 | dirent_ = dirent; | |
126 | 36 | } | |
127 | 36 | void Release() { | |
128 | 36 | const int retval = pthread_mutex_unlock(&access_mutex_); | |
129 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | assert(retval == 0); |
130 | 36 | } | |
131 | |||
132 | 36 | virtual ~BaseMagicXattr() { } | |
133 | |||
134 | // how many chars per page (with some leeway). system maximum would be 64k | ||
135 | static const uint32_t kMaxCharsPerPage = 40000; | ||
136 | |||
137 | protected: | ||
138 | /** | ||
139 | * This function is used to obtain the necessary information while | ||
140 | * inside FuseRemounter::fence(), which should prevent data races. | ||
141 | */ | ||
142 | 36 | virtual bool PrepareValueFenced() { return true; } | |
143 | ✗ | virtual void FinalizeValue() { } | |
144 | |||
145 | std::string HeaderMultipageHuman(uint32_t requested_page); | ||
146 | |||
147 | MagicXattrManager *xattr_mgr_; | ||
148 | PathString path_; | ||
149 | catalog::DirectoryEntry *dirent_; | ||
150 | |||
151 | pthread_mutex_t access_mutex_; | ||
152 | bool is_protected_; | ||
153 | std::vector<std::string> result_pages_; | ||
154 | }; | ||
155 | |||
156 | /** | ||
157 | * This wrapper ensures that the attribute instance "ptr_" is | ||
158 | * released after the user finishes using it (on wrapper destruction). | ||
159 | */ | ||
160 | class MagicXattrRAIIWrapper : public SingleCopy { | ||
161 | public: | ||
162 | inline MagicXattrRAIIWrapper() : ptr_(NULL) { } | ||
163 | |||
164 | inline explicit MagicXattrRAIIWrapper(BaseMagicXattr *ptr, | ||
165 | PathString path, | ||
166 | catalog::DirectoryEntry *d) | ||
167 | : ptr_(ptr) { | ||
168 | if (ptr_ != NULL) | ||
169 | ptr_->Lock(path, d); | ||
170 | } | ||
171 | /// Wraps around a BaseMagicXattr* that is already locked (or NULL) | ||
172 | 36 | inline explicit MagicXattrRAIIWrapper(BaseMagicXattr *ptr) : ptr_(ptr) { } | |
173 | |||
174 | 36 | inline ~MagicXattrRAIIWrapper() { | |
175 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | if (ptr_ != NULL) |
176 | 36 | ptr_->Release(); | |
177 | 36 | } | |
178 | |||
179 | 81 | inline BaseMagicXattr *operator->() const { return ptr_; } | |
180 | 36 | inline bool IsNull() const { return ptr_ == NULL; } | |
181 | inline BaseMagicXattr *Move() { | ||
182 | BaseMagicXattr *ret = ptr_; | ||
183 | ptr_ = NULL; | ||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | protected: | ||
188 | BaseMagicXattr *ptr_; | ||
189 | }; | ||
190 | |||
191 | class WithHashMagicXattr : public BaseMagicXattr { | ||
192 | 18 | virtual MagicXattrFlavor GetXattrFlavor() { return kXattrWithHash; } | |
193 | }; | ||
194 | |||
195 | class RegularMagicXattr : public BaseMagicXattr { | ||
196 | 45 | virtual MagicXattrFlavor GetXattrFlavor() { return kXattrRegular; } | |
197 | }; | ||
198 | |||
199 | class ExternalMagicXattr : public BaseMagicXattr { | ||
200 | 9 | virtual MagicXattrFlavor GetXattrFlavor() { return kXattrExternal; } | |
201 | }; | ||
202 | |||
203 | class SymlinkMagicXattr : public BaseMagicXattr { | ||
204 | 18 | virtual MagicXattrFlavor GetXattrFlavor() { return kXattrSymlink; } | |
205 | }; | ||
206 | |||
207 | /** | ||
208 | * This class is acting as a user entry point for magic extended attributes. | ||
209 | * It instantiates all defined attributes in the constructor. | ||
210 | * Users can: | ||
211 | * 1. Register additional attributes | ||
212 | * 2. Get a string containing zero-byte delimited list of attribute names | ||
213 | * (used in "cvmfs.cc") | ||
214 | * 3. Get an attribute by name. Specifically, get a RAII wrapper around | ||
215 | * a singleton attribute instance. This means that the attribute instance | ||
216 | * registered with the manager does not get cloned or copied inside Get(). | ||
217 | * Instead, member variables are set and the original instance is returned. | ||
218 | * A mutex prevents from race conditions in case of concurrent access. | ||
219 | */ | ||
220 | class MagicXattrManager : public SingleCopy { | ||
221 | public: | ||
222 | enum EVisibility { | ||
223 | kVisibilityAlways, | ||
224 | kVisibilityNever, | ||
225 | kVisibilityRootOnly | ||
226 | }; | ||
227 | |||
228 | MagicXattrManager(MountPoint *mountpoint, EVisibility visibility, | ||
229 | const std::set<std::string> &protected_xattrs, | ||
230 | const std::set<gid_t> &privileged_xattr_gids); | ||
231 | /// The returned BaseMagicXattr* is supposed to be wrapped by a | ||
232 | /// MagicXattrRAIIWrapper | ||
233 | BaseMagicXattr *GetLocked(const std::string &name, PathString path, | ||
234 | catalog::DirectoryEntry *d); | ||
235 | std::string GetListString(catalog::DirectoryEntry *dirent); | ||
236 | /** | ||
237 | * Registers a new extended attribute. | ||
238 | * Will fail if called after Freeze(). | ||
239 | */ | ||
240 | void Register(const std::string &name, BaseMagicXattr *magic_xattr); | ||
241 | |||
242 | /** | ||
243 | * Freezes the current setup of MagicXattrManager. | ||
244 | * No new extended attributes can be added. | ||
245 | * Only after freezing MagicXattrManager can registered attributes be | ||
246 | * accessed. | ||
247 | */ | ||
248 | 9 | void Freeze() { | |
249 | 9 | is_frozen_ = true; | |
250 | 9 | SanityCheckProtectedXattrs(); | |
251 | 9 | } | |
252 | bool IsPrivilegedGid(gid_t gid); | ||
253 | |||
254 | |||
255 | 54 | EVisibility visibility() { return visibility_; } | |
256 | std::set<gid_t> privileged_xattr_gids() { return privileged_xattr_gids_; } | ||
257 | 18 | MountPoint *mount_point() { return mount_point_; } | |
258 | 18 | bool is_frozen() const { return is_frozen_; } | |
259 | |||
260 | protected: | ||
261 | std::map<std::string, BaseMagicXattr *> xattr_list_; | ||
262 | MountPoint *mount_point_; | ||
263 | EVisibility visibility_; | ||
264 | |||
265 | // privileged_xattr_gids_ contains the (fuse) gids that | ||
266 | // can access xattrs that are part of protected_xattrs_ | ||
267 | const std::set<std::string> protected_xattrs_; | ||
268 | const std::set<gid_t> privileged_xattr_gids_; | ||
269 | |||
270 | private: | ||
271 | bool is_frozen_; | ||
272 | |||
273 | void SanityCheckProtectedXattrs(); | ||
274 | }; | ||
275 | |||
276 | class AuthzMagicXattr : public BaseMagicXattr { | ||
277 | virtual bool PrepareValueFenced(); | ||
278 | virtual void FinalizeValue(); | ||
279 | |||
280 | virtual MagicXattrFlavor GetXattrFlavor(); | ||
281 | }; | ||
282 | |||
283 | class CatalogCountersMagicXattr : public BaseMagicXattr { | ||
284 | std::string subcatalog_path_; | ||
285 | shash::Any hash_; | ||
286 | catalog::Counters counters_; | ||
287 | |||
288 | virtual bool PrepareValueFenced(); | ||
289 | virtual void FinalizeValue(); | ||
290 | }; | ||
291 | |||
292 | class ChunkListMagicXattr : public RegularMagicXattr { | ||
293 | virtual bool PrepareValueFenced(); | ||
294 | virtual void FinalizeValue(); | ||
295 | |||
296 | private: | ||
297 | std::vector<std::string> chunk_list_; | ||
298 | }; | ||
299 | |||
300 | class ChunksMagicXattr : public RegularMagicXattr { | ||
301 | uint64_t n_chunks_; | ||
302 | |||
303 | virtual bool PrepareValueFenced(); | ||
304 | virtual void FinalizeValue(); | ||
305 | }; | ||
306 | |||
307 | class CompressionMagicXattr : public RegularMagicXattr { | ||
308 | virtual bool PrepareValueFenced(); | ||
309 | virtual void FinalizeValue(); | ||
310 | }; | ||
311 | |||
312 | class DirectIoMagicXattr : public RegularMagicXattr { | ||
313 | virtual bool PrepareValueFenced(); | ||
314 | virtual void FinalizeValue(); | ||
315 | }; | ||
316 | |||
317 | class ExternalFileMagicXattr : public RegularMagicXattr { | ||
318 | virtual bool PrepareValueFenced(); | ||
319 | virtual void FinalizeValue(); | ||
320 | }; | ||
321 | |||
322 | class ExternalHostMagicXattr : public BaseMagicXattr { | ||
323 | virtual void FinalizeValue(); | ||
324 | }; | ||
325 | |||
326 | class ExternalTimeoutMagicXattr : public BaseMagicXattr { | ||
327 | virtual void FinalizeValue(); | ||
328 | }; | ||
329 | |||
330 | class FqrnMagicXattr : public BaseMagicXattr { | ||
331 | virtual void FinalizeValue(); | ||
332 | }; | ||
333 | |||
334 | class HashMagicXattr : public WithHashMagicXattr { | ||
335 | virtual bool PrepareValueFenced(); | ||
336 | virtual void FinalizeValue(); | ||
337 | }; | ||
338 | |||
339 | class HostMagicXattr : public BaseMagicXattr { | ||
340 | virtual void FinalizeValue(); | ||
341 | }; | ||
342 | |||
343 | class HostListMagicXattr : public BaseMagicXattr { | ||
344 | virtual void FinalizeValue(); | ||
345 | }; | ||
346 | |||
347 | class LHashMagicXattr : public WithHashMagicXattr { | ||
348 | virtual bool PrepareValueFenced(); | ||
349 | virtual void FinalizeValue(); | ||
350 | }; | ||
351 | |||
352 | class LogBufferXattr : public BaseMagicXattr { | ||
353 | public: | ||
354 | LogBufferXattr(); | ||
355 | |||
356 | private: | ||
357 | const unsigned int kMaxLogLine = 4096; // longer log lines are trimmed | ||
358 | // Generating the log buffer report involves 64 string copies. To mitigate | ||
359 | // memory fragmentation and performance loss, throttle the use of this | ||
360 | // attribute a little. | ||
361 | BackoffThrottle throttle_; | ||
362 | |||
363 | virtual void FinalizeValue(); | ||
364 | }; | ||
365 | |||
366 | class NCleanup24MagicXattr : public BaseMagicXattr { | ||
367 | virtual void FinalizeValue(); | ||
368 | }; | ||
369 | |||
370 | class NClgMagicXattr : public BaseMagicXattr { | ||
371 | int n_catalogs_; | ||
372 | |||
373 | virtual bool PrepareValueFenced(); | ||
374 | virtual void FinalizeValue(); | ||
375 | }; | ||
376 | |||
377 | class NDirOpenMagicXattr : public BaseMagicXattr { | ||
378 | virtual void FinalizeValue(); | ||
379 | }; | ||
380 | |||
381 | class NDownloadMagicXattr : public BaseMagicXattr { | ||
382 | virtual void FinalizeValue(); | ||
383 | }; | ||
384 | |||
385 | class NIOErrMagicXattr : public BaseMagicXattr { | ||
386 | virtual void FinalizeValue(); | ||
387 | }; | ||
388 | |||
389 | class NOpenMagicXattr : public BaseMagicXattr { | ||
390 | virtual void FinalizeValue(); | ||
391 | }; | ||
392 | |||
393 | class HitrateMagicXattr : public BaseMagicXattr { | ||
394 | virtual void FinalizeValue(); | ||
395 | }; | ||
396 | |||
397 | class ProxyMagicXattr : public BaseMagicXattr { | ||
398 | virtual void FinalizeValue(); | ||
399 | }; | ||
400 | |||
401 | class ProxyListMagicXattr : public BaseMagicXattr { | ||
402 | virtual void FinalizeValue(); | ||
403 | }; | ||
404 | |||
405 | class ProxyListExternalMagicXattr : public BaseMagicXattr { | ||
406 | virtual void FinalizeValue(); | ||
407 | }; | ||
408 | |||
409 | class PubkeysMagicXattr : public BaseMagicXattr { | ||
410 | FRIEND_TEST(T_MagicXattr, MultiPageMachineModeXattr); | ||
411 | FRIEND_TEST(T_MagicXattr, MultiPageHumanModeXattr); | ||
412 | |||
413 | std::vector<std::string> pubkeys_; | ||
414 | |||
415 | virtual bool PrepareValueFenced(); | ||
416 | virtual void FinalizeValue(); | ||
417 | }; | ||
418 | |||
419 | class RawlinkMagicXattr : public SymlinkMagicXattr { | ||
420 | virtual bool PrepareValueFenced(); | ||
421 | virtual void FinalizeValue(); | ||
422 | }; | ||
423 | |||
424 | class RepoCountersMagicXattr : public BaseMagicXattr { | ||
425 | catalog::Counters counters_; | ||
426 | |||
427 | virtual bool PrepareValueFenced(); | ||
428 | virtual void FinalizeValue(); | ||
429 | }; | ||
430 | |||
431 | class RepoMetainfoMagicXattr : public BaseMagicXattr { | ||
432 | static uint64_t kMaxMetainfoLength; | ||
433 | |||
434 | shash::Any metainfo_hash_; | ||
435 | std::string error_reason_; | ||
436 | |||
437 | virtual bool PrepareValueFenced(); | ||
438 | virtual void FinalizeValue(); | ||
439 | }; | ||
440 | |||
441 | class RevisionMagicXattr : public BaseMagicXattr { | ||
442 | uint64_t revision_; | ||
443 | |||
444 | virtual bool PrepareValueFenced(); | ||
445 | virtual void FinalizeValue(); | ||
446 | }; | ||
447 | |||
448 | class RootHashMagicXattr : public BaseMagicXattr { | ||
449 | shash::Any root_hash_; | ||
450 | |||
451 | virtual bool PrepareValueFenced(); | ||
452 | virtual void FinalizeValue(); | ||
453 | }; | ||
454 | |||
455 | class RxMagicXattr : public BaseMagicXattr { | ||
456 | virtual void FinalizeValue(); | ||
457 | }; | ||
458 | |||
459 | class SpeedMagicXattr : public BaseMagicXattr { | ||
460 | virtual void FinalizeValue(); | ||
461 | }; | ||
462 | |||
463 | class TagMagicXattr : public BaseMagicXattr { | ||
464 | std::string tag_; | ||
465 | |||
466 | virtual bool PrepareValueFenced(); | ||
467 | virtual void FinalizeValue(); | ||
468 | }; | ||
469 | |||
470 | class TimeoutMagicXattr : public BaseMagicXattr { | ||
471 | virtual void FinalizeValue(); | ||
472 | }; | ||
473 | |||
474 | class TimeoutDirectMagicXattr : public BaseMagicXattr { | ||
475 | virtual void FinalizeValue(); | ||
476 | }; | ||
477 | |||
478 | class TimestampLastIOErrMagicXattr : public BaseMagicXattr { | ||
479 | virtual void FinalizeValue(); | ||
480 | }; | ||
481 | |||
482 | class UsedFdMagicXattr : public BaseMagicXattr { | ||
483 | virtual void FinalizeValue(); | ||
484 | }; | ||
485 | |||
486 | class UsedDirPMagicXattr : public BaseMagicXattr { | ||
487 | virtual void FinalizeValue(); | ||
488 | }; | ||
489 | |||
490 | class VersionMagicXattr : public BaseMagicXattr { | ||
491 | virtual void FinalizeValue(); | ||
492 | }; | ||
493 | |||
494 | class ExternalURLMagicXattr : public ExternalMagicXattr { | ||
495 | virtual bool PrepareValueFenced(); | ||
496 | virtual void FinalizeValue(); | ||
497 | }; | ||
498 | |||
499 | #endif // CVMFS_MAGIC_XATTR_H_ | ||
500 |