GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/libcvmfs_int.cc
Date: 2026-08-02 02:40:19
Exec Total Coverage
Lines: 230 395 58.2%
Branches: 160 500 32.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This is the internal implementation of libcvmfs, not to be exposed
5 * to the code using the library. This code is based heavily on the
6 * fuse module cvmfs.cc.
7 */
8
9 #define ENOATTR ENODATA /**< instead of including attr/xattr.h */
10
11 // clang-format off
12 #include <sys/xattr.h>
13 // clang-format on
14
15 #include "libcvmfs_int.h"
16
17 #include <dirent.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <pthread.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <sys/errno.h>
24 #include <sys/file.h>
25 #include <sys/mount.h>
26 #include <sys/resource.h>
27 #include <sys/stat.h>
28
29 #include <google/dense_hash_map>
30 #ifndef __APPLE__
31 #include <sys/statfs.h>
32 #endif
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37
38 #include <algorithm>
39 #include <cassert>
40 #include <csignal>
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
44 #include <ctime>
45 #include <map>
46 #include <string>
47 #include <vector>
48
49 #include "bundle_mgr.h"
50 #include "cache_posix.h"
51 #include "catalog.h"
52 #include "catalog_mgr_client.h"
53 #include "clientctx.h"
54 #include "compression/compression.h"
55 #include "crypto/crypto_util.h"
56 #include "crypto/hash.h"
57 #include "directory_entry.h"
58 #include "duplex_sqlite3.h" // IWYU pragma: keep
59 #include "fetch.h"
60 #include "libcvmfs.h"
61 #include "lru_md.h"
62 #include "network/download.h"
63 #include "shortstring.h"
64 #include "sqlitevfs.h"
65 #include "util/logging.h"
66 #include "util/posix.h"
67 #include "util/string.h"
68 #include "xattr.h"
69
70 using namespace std; // NOLINT
71
72 // TODO(jblomer): remove. Only needed to satisfy monitor.cc
73 namespace cvmfs {
74 pid_t pid_ = 0;
75 }
76
77
78 LibGlobals *LibGlobals::instance_ = NULL;
79 1605 LibGlobals *LibGlobals::GetInstance() {
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1605 times.
1605 assert(LibGlobals::instance_ != NULL);
81 1605 return LibGlobals::instance_;
82 }
83
84
85 /**
86 * Always creates the singleton, even in case of failure.
87 */
88 732 loader::Failures LibGlobals::Initialize(OptionsManager *options_mgr) {
89
1/2
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
732 LogCvmfs(kLogCvmfs, kLogStdout, "LibCvmfs version %d.%d, revision %d",
90 LIBCVMFS_VERSION_MAJOR, LIBCVMFS_VERSION_MINOR, LIBCVMFS_REVISION);
91
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 732 times.
732 assert(options_mgr != NULL);
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 732 times.
732 assert(instance_ == NULL);
94
1/2
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
732 instance_ = new LibGlobals();
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 732 times.
732 assert(instance_ != NULL);
96
97 // Multi-threaded libcrypto (otherwise done by the loader)
98
1/2
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
732 crypto::SetupLibcryptoMt();
99
100 732 FileSystem::FileSystemInfo fs_info;
101
1/2
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
732 fs_info.name = "libcvmfs";
102 732 fs_info.type = FileSystem::kFsLibrary;
103 732 fs_info.options_mgr = options_mgr;
104
1/2
✓ Branch 1 taken 732 times.
✗ Branch 2 not taken.
732 instance_->file_system_ = FileSystem::Create(fs_info);
105
106
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 694 times.
732 if (instance_->file_system_->boot_status() != loader::kFailOk)
107 38 return instance_->file_system_->boot_status();
108
109 // Maximum number of open files, handled otherwise as root by the fuse loader
110 694 string arg;
111
4/6
✓ Branch 2 taken 694 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 694 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 272 times.
✓ Branch 10 taken 422 times.
694 if (options_mgr->GetValue("CVMFS_NFILES", &arg)) {
112
2/4
✓ Branch 1 taken 272 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 272 times.
✗ Branch 5 not taken.
272 const int retval = SetLimitNoFile(String2Uint64(arg));
113
2/2
✓ Branch 0 taken 194 times.
✓ Branch 1 taken 78 times.
272 if (retval != 0) {
114
2/4
✓ Branch 2 taken 194 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 194 times.
✗ Branch 6 not taken.
194 PrintError("Failed to set maximum number of open files, "
115 "insufficient permissions");
116 194 return loader::kFailPermission;
117 }
118 }
119
120 500 return loader::kFailOk;
121 732 }
122
123
124 730 void LibGlobals::CleanupInstance() {
125
1/2
✓ Branch 0 taken 730 times.
✗ Branch 1 not taken.
730 if (instance_ != NULL) {
126
1/2
✓ Branch 0 taken 730 times.
✗ Branch 1 not taken.
730 delete instance_;
127 730 instance_ = NULL;
128 }
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 730 times.
730 assert(instance_ == NULL);
130 730 }
131
132
133 732 LibGlobals::LibGlobals() : options_mgr_(NULL), file_system_(NULL) { }
134
135
136 730 LibGlobals::~LibGlobals() {
137
1/2
✓ Branch 0 taken 730 times.
✗ Branch 1 not taken.
730 delete file_system_;
138
2/2
✓ Branch 0 taken 347 times.
✓ Branch 1 taken 383 times.
730 delete options_mgr_;
139
140 730 crypto::CleanupLibcryptoMt();
141 730 }
142
143
144 //------------------------------------------------------------------------------
145
146
147 305 LibContext *LibContext::Create(const string &fqrn,
148 OptionsManager *options_mgr) {
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 305 times.
305 assert(options_mgr != NULL);
150 305 LibContext *ctx = new LibContext();
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 305 times.
305 assert(ctx != NULL);
152
153 305 ctx->mount_point_ = MountPoint::Create(
154 fqrn, LibGlobals::GetInstance()->file_system(), options_mgr);
155 // The library never daemonizes, so the prefetcher threads can start
156 // right away (the fuse client defers this until after the fork)
157
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 305 times.
305 if (ctx->mount_point_->bundle_mgr() != nullptr) {
158 ctx->mount_point_->bundle_mgr()->Spawn();
159 }
160 305 return ctx;
161 }
162
163
164 305 LibContext::LibContext() : options_mgr_(NULL), mount_point_(NULL) { }
165
166
167 305 LibContext::~LibContext() {
168
1/2
✓ Branch 0 taken 305 times.
✗ Branch 1 not taken.
305 delete mount_point_;
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 305 times.
305 delete options_mgr_;
170 305 }
171
172 void LibContext::EnableMultiThreaded() {
173 mount_point_->download_mgr()->Spawn();
174 }
175
176 1102 bool LibContext::GetDirentForPath(const PathString &path,
177 catalog::DirectoryEntry *dirent) {
178
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 1102 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1102 times.
1102 if (path.GetLength() == 1 && path.GetChars()[0] == '/') {
179 // root path is expected to be "", not "/"
180 const PathString p;
181 return GetDirentForPath(p, dirent);
182 }
183
1/2
✓ Branch 3 taken 1102 times.
✗ Branch 4 not taken.
1102 const shash::Md5 md5path(path.GetChars(), path.GetLength());
184
3/4
✓ Branch 2 taken 1102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 456 times.
✓ Branch 5 taken 646 times.
1102 if (mount_point_->md5path_cache()->Lookup(md5path, dirent))
185 456 return dirent->GetSpecial() != catalog::kDirentNegative;
186
187 // TODO(jblomer): not twice md5 calculation
188
3/4
✓ Branch 2 taken 646 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 570 times.
✓ Branch 5 taken 76 times.
646 if (mount_point_->catalog_mgr()->LookupPath(path, catalog::kLookupDefault,
189 dirent)) {
190
1/2
✓ Branch 2 taken 570 times.
✗ Branch 3 not taken.
570 mount_point_->md5path_cache()->Insert(md5path, *dirent);
191 570 return true;
192 }
193
194
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry");
195 // Only cache real ENOENT errors, not catalog load errors
196
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 if (dirent->GetSpecial() == catalog::kDirentNegative)
197
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 mount_point_->md5path_cache()->InsertNegative(md5path);
198
199 76 return false;
200 }
201
202
203 608 void LibContext::AppendStringToList(char const *str,
204 char ***buf,
205 size_t *listlen,
206 size_t *buflen) {
207
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 456 times.
608 if (*listlen + 1 >= *buflen) {
208 152 const size_t newbuflen = (*listlen) * 2 + 5;
209 152 *buf = reinterpret_cast<char **>(realloc(*buf, sizeof(char *) * newbuflen));
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 assert(*buf);
211 152 *buflen = newbuflen;
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 assert(*listlen < *buflen);
213 }
214
2/2
✓ Branch 0 taken 494 times.
✓ Branch 1 taken 114 times.
608 if (str) {
215 494 (*buf)[(*listlen)] = strdup(str);
216 // null-terminate the list
217 494 (*buf)[++(*listlen)] = NULL;
218 } else {
219 114 (*buf)[(*listlen)] = NULL;
220 }
221 608 }
222
223
224 152 void LibContext::AppendStatToList(const cvmfs_stat_t st,
225 cvmfs_stat_t **buf,
226 size_t *listlen,
227 size_t *buflen) {
228
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 114 times.
152 if (*listlen + 1 >= *buflen) {
229 38 const size_t newbuflen = (*listlen) * 2 + 5;
230 38 *buf = reinterpret_cast<cvmfs_stat_t *>(
231 38 realloc(*buf, sizeof(cvmfs_stat_t) * newbuflen));
232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(*buf);
233 38 *buflen = newbuflen;
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 assert(*listlen < *buflen);
235 }
236 152 (*buf)[(*listlen)].info = st.info;
237 152 (*buf)[(*listlen)++].name = st.name;
238 152 }
239
240 912 int LibContext::GetAttr(const char *c_path, struct stat *info) {
241
1/2
✓ Branch 1 taken 912 times.
✗ Branch 2 not taken.
912 perf::Inc(file_system()->n_fs_stat());
242 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
243
1/2
✓ Branch 4 taken 912 times.
✗ Branch 5 not taken.
912 &default_interrupt_cue_);
244
245
1/2
✓ Branch 1 taken 912 times.
✗ Branch 2 not taken.
912 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for path: %s", c_path);
246
247 912 PathString p;
248
1/2
✓ Branch 1 taken 912 times.
✗ Branch 2 not taken.
912 p.Assign(c_path, strlen(c_path));
249
250
1/2
✓ Branch 1 taken 912 times.
✗ Branch 2 not taken.
912 catalog::DirectoryEntry dirent;
251
1/2
✓ Branch 1 taken 912 times.
✗ Branch 2 not taken.
912 const bool found = GetDirentForPath(p, &dirent);
252
253
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 874 times.
912 if (!found) {
254 38 return -ENOENT;
255 }
256
257
1/2
✓ Branch 1 taken 874 times.
✗ Branch 2 not taken.
874 *info = dirent.GetStatStructure();
258 874 return 0;
259 912 }
260
261 76 void LibContext::CvmfsAttrFromDirent(const catalog::DirectoryEntry dirent,
262 struct cvmfs_attr *attr) {
263 76 attr->st_ino = dirent.inode();
264 76 attr->st_mode = dirent.mode();
265 76 attr->st_nlink = dirent.linkcount();
266 76 attr->st_uid = dirent.uid();
267 76 attr->st_gid = dirent.gid();
268 76 attr->st_rdev = dirent.rdev();
269 76 attr->st_size = dirent.size();
270 76 attr->mtime = dirent.mtime();
271
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 attr->cvm_checksum = strdup(dirent.checksum().ToString().c_str());
272 76 attr->cvm_symlink = strdup(dirent.symlink().c_str());
273 76 attr->cvm_name = strdup(dirent.name().c_str());
274 76 attr->cvm_xattrs = NULL;
275 76 }
276
277
278 114 int LibContext::GetExtAttr(const char *c_path, struct cvmfs_attr *info) {
279 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
280
1/2
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
114 &default_interrupt_cue_);
281
282
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for path: %s", c_path);
283
284 114 PathString p;
285
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 p.Assign(c_path, strlen(c_path));
286
287
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 catalog::DirectoryEntry dirent;
288
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 const bool found = GetDirentForPath(p, &dirent);
289
290
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 76 times.
114 if (!found) {
291 38 return -ENOENT;
292 }
293
294
2/4
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
76 CvmfsAttrFromDirent(dirent, info);
295 // Chunked files without bulk hash need to be treated specially
296 76 info->cvm_nchunks = 0;
297 76 info->cvm_is_hash_artificial = 0;
298
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 38 times.
76 if (dirent.IsRegular()) {
299 38 info->cvm_nchunks = 1;
300
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (dirent.IsChunkedFile()) {
301 FileChunkList *chunks = new FileChunkList();
302 mount_point_->catalog_mgr()->ListFileChunks(p, dirent.hash_algorithm(),
303 chunks);
304 assert(!chunks->IsEmpty());
305 info->cvm_nchunks = chunks->size();
306 if (dirent.checksum().IsNull()) {
307 info->cvm_is_hash_artificial = 1;
308 free(info->cvm_checksum);
309 FileChunkReflist chunks_reflist(
310 chunks, p, dirent.compression_algorithm(), dirent.IsExternalFile());
311 const std::string hash_str = chunks_reflist.HashChunkList().ToString();
312 info->cvm_checksum = strdup(hash_str.c_str());
313 }
314 delete chunks;
315 }
316 }
317
318
2/4
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 76 times.
✗ Branch 6 not taken.
76 info->cvm_parent = strdup(GetParentPath(c_path).c_str());
319
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 76 times.
76 if (dirent.HasXattrs()) {
320 XattrList *xattrs = new XattrList();
321 mount_point_->catalog_mgr()->LookupXattrs(p, xattrs);
322 info->cvm_xattrs = xattrs;
323 }
324 76 return 0;
325 114 }
326
327
328 int LibContext::Readlink(const char *c_path, char *buf, size_t size) {
329 perf::Inc(file_system()->n_fs_readlink());
330 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_readlink on path: %s", c_path);
331 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
332 &default_interrupt_cue_);
333
334 PathString p;
335 p.Assign(c_path, strlen(c_path));
336
337 catalog::DirectoryEntry dirent;
338 const bool found = GetDirentForPath(p, &dirent);
339
340 if (!found) {
341 return -ENOENT;
342 }
343
344 if (!dirent.IsLink()) {
345 return -EINVAL;
346 }
347
348 const unsigned len = (dirent.symlink().GetLength() >= size)
349 ? size
350 : dirent.symlink().GetLength() + 1;
351 strncpy(buf, dirent.symlink().c_str(), len - 1);
352 buf[len - 1] = '\0';
353
354 return 0;
355 }
356
357 38 int LibContext::ListDirectory(const char *c_path,
358 char ***buf,
359 size_t *listlen,
360 size_t *buflen,
361 bool self_reference) {
362
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_listdir on path: %s", c_path);
363 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
364
1/2
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 &default_interrupt_cue_);
365
366
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
38 if (c_path[0] == '/' && c_path[1] == '\0') {
367 // root path is expected to be "", not "/"
368 c_path = "";
369 }
370
371 38 PathString path;
372
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 path.Assign(c_path, strlen(c_path));
373
374
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 catalog::DirectoryEntry d;
375
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const bool found = GetDirentForPath(path, &d);
376
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (!found) {
378 return -ENOENT;
379 }
380
381
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (!d.IsDirectory()) {
382 return -ENOTDIR;
383 }
384
385 38 AppendStringToList(NULL, buf, listlen, buflen);
386
387 // Build listing
388
389
1/2
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
38 if (self_reference) {
390 // Add current directory link
391 38 AppendStringToList(".", buf, listlen, buflen);
392
393 // Add parent directory link
394
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const catalog::DirectoryEntry p;
395
2/4
✓ Branch 3 taken 38 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 38 times.
✗ Branch 6 not taken.
38 if (d.inode() != mount_point_->catalog_mgr()->GetRootInode()) {
396 38 AppendStringToList("..", buf, listlen, buflen);
397 }
398 38 }
399
400 // Add all names
401
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 catalog::StatEntryList listing_from_catalog;
402
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38 times.
38 if (!mount_point_->catalog_mgr()->ListingStat(path, &listing_from_catalog)) {
403 return -EIO;
404 }
405
2/2
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 38 times.
114 for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
406 76 AppendStringToList(listing_from_catalog.AtPtr(i)->name.c_str(), buf,
407 listlen, buflen);
408 }
409
410 38 return 0;
411 38 }
412
413 38 int LibContext::ListDirectoryStat(const char *c_path,
414 cvmfs_stat_t **buf,
415 size_t *listlen,
416 size_t *buflen) {
417
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_listdir_stat on path: %s", c_path);
418 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
419
1/2
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
38 &default_interrupt_cue_);
420
421
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
38 if (c_path[0] == '/' && c_path[1] == '\0') {
422 // root path is expected to be "", not "/"
423 c_path = "";
424 }
425
426 38 PathString path;
427
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 path.Assign(c_path, strlen(c_path));
428
429
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 catalog::DirectoryEntry d;
430
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 const bool found = GetDirentForPath(path, &d);
431
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 if (!found) {
433 return -ENOENT;
434 }
435
436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
38 if (!d.IsDirectory()) {
437 return -ENOTDIR;
438 }
439
440 // Build listing
441
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 catalog::StatEntryList listing_from_catalog;
442
2/4
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38 times.
38 if (!mount_point_->catalog_mgr()->ListingStat(path, &listing_from_catalog)) {
443 return -EIO;
444 }
445
2/2
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 38 times.
190 for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
446 cvmfs_stat_t st;
447 152 st.info = listing_from_catalog.AtPtr(i)->info;
448 152 st.name = strdup(listing_from_catalog.AtPtr(i)->name.c_str());
449 152 AppendStatToList(st, buf, listlen, buflen);
450 }
451
452 38 return 0;
453 38 }
454
455 114 int LibContext::GetNestedCatalogAttr(const char *c_path,
456 struct cvmfs_nc_attr *nc_attr) {
457 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
458
1/2
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
114 &default_interrupt_cue_);
459
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_stat_nc (cvmfs_nc_attr) : %s", c_path);
460
461 114 PathString p;
462
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 p.Assign(c_path, strlen(c_path));
463
464 114 PathString mountpoint;
465
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 shash::Any hash;
466 uint64_t size;
467
468 // Find the nested catalog from the root catalog
469
1/2
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 const bool found = mount_point_->catalog_mgr()->LookupNested(p, &mountpoint,
470 &hash, &size);
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 if (!found) {
472 return -ENOENT;
473 }
474
475 114 std::string subcat_path;
476
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 shash::Any tmp_hash;
477 114 std::map<std::string, uint64_t> counters = mount_point_->catalog_mgr()
478
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 ->LookupCounters(
479 p, &subcat_path, &tmp_hash)
480
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 .GetValues();
481
482 // Set values of the passed structure
483
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 nc_attr->mountpoint = strdup(mountpoint.ToString().c_str());
484
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 nc_attr->hash = strdup(hash.ToString().c_str());
485 114 nc_attr->size = size;
486
487
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_regular = counters["regular"];
488
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_symlink = counters["symlink"];
489
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_special = counters["special"];
490
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_dir = counters["dir"];
491
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_nested = counters["nested"];
492
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_chunked = counters["chunked"];
493
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_chunks = counters["chunks"];
494
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_file_size = counters["file_size"];
495
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_chunked_size = counters["chunked_size"];
496
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_xattr = counters["xattr"];
497
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_external = counters["external"];
498
2/4
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 114 times.
✗ Branch 6 not taken.
114 nc_attr->ctr_external_file_size = counters["external_file_size"];
499 114 return 0;
500 114 }
501
502
503 76 int LibContext::ListNestedCatalogs(const char *c_path,
504 char ***buf,
505 size_t *buflen) {
506 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
507
1/2
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
76 &default_interrupt_cue_);
508
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_list_nc on path: %s", c_path);
509
510
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (c_path[0] == '/' && c_path[1] == '\0') {
511 // root path is expected to be "", not "/"
512 c_path = "";
513 }
514
515 76 PathString path;
516
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 path.Assign(c_path, strlen(c_path));
517
518 76 std::vector<PathString> skein;
519
1/2
✓ Branch 2 taken 76 times.
✗ Branch 3 not taken.
76 const bool retval = mount_point_->catalog_mgr()->ListCatalogSkein(path,
520 &skein);
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (!retval) {
522 LogCvmfs(kLogCvmfs, kLogDebug,
523 "cvmfs_list_nc failed to find skein of path: %s", c_path);
524 return 1;
525 }
526
527 76 size_t listlen = 0;
528 76 AppendStringToList(NULL, buf, &listlen, buflen);
529
530
2/2
✓ Branch 1 taken 342 times.
✓ Branch 2 taken 76 times.
418 for (unsigned i = 0; i < skein.size(); i++) {
531
1/2
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
342 AppendStringToList(skein.at(i).c_str(), buf, &listlen, buflen);
532 }
533
534 76 return 0;
535 76 }
536
537
538 int LibContext::Open(const char *c_path) {
539 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_open on path: %s", c_path);
540 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
541 &default_interrupt_cue_);
542
543 int fd = -1;
544 catalog::DirectoryEntry dirent;
545 PathString path;
546 path.Assign(c_path, strlen(c_path));
547
548 const bool found = GetDirentForPath(path, &dirent);
549
550 if (!found) {
551 return -ENOENT;
552 }
553
554 BundleMgr *bundle_mgr = mount_point_->bundle_mgr();
555 if (bundle_mgr != nullptr && dirent.IsBundleTrigger()) {
556 // Hand the trigger over to the long-lived prefetcher; spec loading and
557 // dependency downloads happen on its background threads, so the open()
558 // does not wait for them.
559 bundle_mgr->ScheduleTrigger(path);
560 }
561
562 if (dirent.IsChunkedFile()) {
563 LogCvmfs(kLogCvmfs, kLogDebug,
564 "chunked file %s opened (download delayed to read() call)",
565 path.c_str());
566
567 FileChunkList *chunks = new FileChunkList();
568 if (!mount_point_->catalog_mgr()->ListFileChunks(
569 path, dirent.hash_algorithm(), chunks)
570 || chunks->IsEmpty()) {
571 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
572 "file %s is marked as "
573 "'chunked', but no chunks found.",
574 path.c_str());
575 file_system()->io_error_info()->AddIoError();
576 delete chunks;
577 return -EIO;
578 }
579
580 fd = mount_point_->simple_chunk_tables()->Add(FileChunkReflist(
581 chunks, path, dirent.compression_algorithm(), dirent.IsExternalFile()));
582 return fd | kFdChunked;
583 }
584
585 cvmfs::Fetcher *this_fetcher = dirent.IsExternalFile()
586 ? mount_point_->external_fetcher()
587 : mount_point_->fetcher();
588 CacheManager::Label label;
589 label.path = std::string(path.GetChars(), path.GetLength());
590 label.size = dirent.size();
591 label.zip_algorithm = dirent.compression_algorithm();
592 if (dirent.IsExternalFile())
593 label.flags |= CacheManager::kLabelExternal;
594 fd = this_fetcher->Fetch(
595 CacheManager::LabeledObject(dirent.checksum(), label));
596 perf::Inc(file_system()->n_fs_open());
597
598 if (fd >= 0) {
599 LogCvmfs(kLogCvmfs, kLogDebug, "file %s opened (fd %d)", path.c_str(), fd);
600 return fd;
601 } else {
602 LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
603 "failed to open path: %s, CAS key %s, error code %d", c_path,
604 dirent.checksum().ToString().c_str(), errno);
605 if (errno == EMFILE) {
606 return -EMFILE;
607 }
608 }
609
610 file_system()->io_error_info()->AddIoError();
611 return fd;
612 }
613
614
615 int64_t LibContext::Pread(int fd, void *buf, uint64_t size, uint64_t off) {
616 if (fd & kFdChunked) {
617 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
618 &default_interrupt_cue_);
619 const int chunk_handle = fd & ~kFdChunked;
620 SimpleChunkTables::OpenChunks
621 open_chunks = mount_point_->simple_chunk_tables()->Get(chunk_handle);
622 FileChunkList *chunk_list = open_chunks.chunk_reflist.list;
623 const zlib::Algorithms compression_alg = open_chunks.chunk_reflist
624 .compression_alg;
625 if (chunk_list == NULL)
626 return -EBADF;
627
628 // Fetch all needed chunks and read the requested data
629 unsigned chunk_idx = open_chunks.chunk_reflist.FindChunkIdx(off);
630 uint64_t overall_bytes_fetched = 0;
631 off_t offset_in_chunk = off - chunk_list->AtPtr(chunk_idx)->offset();
632 do {
633 // Open file descriptor to chunk
634 ChunkFd *chunk_fd = open_chunks.chunk_fd;
635 if ((chunk_fd->fd == -1) || (chunk_fd->chunk_idx != chunk_idx)) {
636 if (chunk_fd->fd != -1)
637 file_system()->cache_mgr()->Close(chunk_fd->fd);
638 cvmfs::Fetcher *this_fetcher = open_chunks.chunk_reflist.external_data
639 ? mount_point_->external_fetcher()
640 : mount_point_->fetcher();
641 CacheManager::Label label;
642 label.path = std::string(open_chunks.chunk_reflist.path.GetChars(),
643 open_chunks.chunk_reflist.path.GetLength());
644 label.size = chunk_list->AtPtr(chunk_idx)->size();
645 label.zip_algorithm = compression_alg;
646 label.flags |= CacheManager::kLabelChunked;
647 if (open_chunks.chunk_reflist.external_data) {
648 label.flags |= CacheManager::kLabelExternal;
649 label.range_offset = chunk_list->AtPtr(chunk_idx)->offset();
650 }
651 chunk_fd->fd = this_fetcher->Fetch(CacheManager::LabeledObject(
652 chunk_list->AtPtr(chunk_idx)->content_hash(), label));
653 if (chunk_fd->fd < 0) {
654 chunk_fd->fd = -1;
655 return -EIO;
656 }
657 chunk_fd->chunk_idx = chunk_idx;
658 }
659
660 LogCvmfs(kLogCvmfs, kLogDebug, "reading from chunk fd %d", chunk_fd->fd);
661 // Read data from chunk
662 const size_t bytes_to_read = size - overall_bytes_fetched;
663 const size_t remaining_bytes_in_chunk = chunk_list->AtPtr(chunk_idx)
664 ->size()
665 - offset_in_chunk;
666 const size_t bytes_to_read_in_chunk = std::min(bytes_to_read,
667 remaining_bytes_in_chunk);
668 const int64_t bytes_fetched = file_system()->cache_mgr()->Pread(
669 chunk_fd->fd,
670 reinterpret_cast<char *>(buf) + overall_bytes_fetched,
671 bytes_to_read_in_chunk,
672 offset_in_chunk);
673
674 if (bytes_fetched < 0) {
675 LogCvmfs(kLogCvmfs, kLogSyslogErr, "read err no %ld (%s)",
676 bytes_fetched,
677 open_chunks.chunk_reflist.path.ToString().c_str());
678 return -bytes_fetched;
679 }
680 overall_bytes_fetched += bytes_fetched;
681
682 // Proceed to the next chunk to keep on reading data
683 ++chunk_idx;
684 offset_in_chunk = 0;
685 } while ((overall_bytes_fetched < size)
686 && (chunk_idx < chunk_list->size()));
687 return overall_bytes_fetched;
688 } else {
689 return file_system()->cache_mgr()->Pread(fd, buf, size, off);
690 }
691 }
692
693
694 int LibContext::Close(int fd) {
695 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_close on file number: %d", fd);
696 if (fd & kFdChunked) {
697 const int chunk_handle = fd & ~kFdChunked;
698 const SimpleChunkTables::OpenChunks
699 open_chunks = mount_point_->simple_chunk_tables()->Get(chunk_handle);
700 if (open_chunks.chunk_reflist.list == NULL)
701 return -EBADF;
702 if (open_chunks.chunk_fd->fd != -1)
703 file_system()->cache_mgr()->Close(open_chunks.chunk_fd->fd);
704 mount_point_->simple_chunk_tables()->Release(chunk_handle);
705 } else {
706 file_system()->cache_mgr()->Close(fd);
707 }
708 return 0;
709 }
710
711 2 int LibContext::Remount() {
712 2 LogCvmfs(kLogCvmfs, kLogDebug, "remounting root catalog");
713 2 catalog::LoadReturn retval = mount_point_->catalog_mgr()->RemountDryrun();
714
715
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 switch (retval) {
716 case catalog::kLoadUp2Date:
717 LogCvmfs(kLogCvmfs, kLogDebug, "catalog up to date");
718 return 0;
719
720 2 case catalog::kLoadNew:
721 2 retval = mount_point_->catalog_mgr()->Remount();
722
723
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (retval != catalog::kLoadUp2Date && retval != catalog::kLoadNew) {
724 LogCvmfs(kLogCvmfs, kLogDebug,
725 "Remount requested to switch catalog but failed");
726 return -1;
727 }
728
729 2 mount_point_->ReEvaluateAuthz();
730 2 LogCvmfs(kLogCvmfs, kLogDebug, "switched to catalog revision %" PRIu64,
731 2 mount_point_->catalog_mgr()->GetRevision());
732 2 return 0;
733
734 default:
735 return -1;
736 }
737 }
738
739
740 3 uint64_t LibContext::GetRevision() {
741 3 return mount_point_->catalog_mgr()->GetRevision();
742 }
743