GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/libcvmfs_int.cc
Date: 2026-08-16 02:40:26
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 141 LibGlobals *LibGlobals::GetInstance() {
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 assert(LibGlobals::instance_ != NULL);
81 141 return LibGlobals::instance_;
82 }
83
84
85 /**
86 * Always creates the singleton, even in case of failure.
87 */
88 64 loader::Failures LibGlobals::Initialize(OptionsManager *options_mgr) {
89
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 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 64 times.
64 assert(options_mgr != NULL);
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(instance_ == NULL);
94
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 instance_ = new LibGlobals();
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 assert(instance_ != NULL);
96
97 // Multi-threaded libcrypto (otherwise done by the loader)
98
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 crypto::SetupLibcryptoMt();
99
100 64 FileSystem::FileSystemInfo fs_info;
101
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 fs_info.name = "libcvmfs";
102 64 fs_info.type = FileSystem::kFsLibrary;
103 64 fs_info.options_mgr = options_mgr;
104
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 instance_->file_system_ = FileSystem::Create(fs_info);
105
106
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 61 times.
64 if (instance_->file_system_->boot_status() != loader::kFailOk)
107 3 return instance_->file_system_->boot_status();
108
109 // Maximum number of open files, handled otherwise as root by the fuse loader
110 61 string arg;
111
4/6
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 61 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 25 times.
✓ Branch 10 taken 36 times.
61 if (options_mgr->GetValue("CVMFS_NFILES", &arg)) {
112
2/4
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25 times.
✗ Branch 5 not taken.
25 const int retval = SetLimitNoFile(String2Uint64(arg));
113
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 10 times.
25 if (retval != 0) {
114
2/4
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
15 PrintError("Failed to set maximum number of open files, "
115 "insufficient permissions");
116 15 return loader::kFailPermission;
117 }
118 }
119
120 46 return loader::kFailOk;
121 64 }
122
123
124 62 void LibGlobals::CleanupInstance() {
125
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 if (instance_ != NULL) {
126
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 delete instance_;
127 62 instance_ = NULL;
128 }
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 assert(instance_ == NULL);
130 62 }
131
132
133 64 LibGlobals::LibGlobals() : options_mgr_(NULL), file_system_(NULL) { }
134
135
136 62 LibGlobals::~LibGlobals() {
137
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 delete file_system_;
138
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 32 times.
62 delete options_mgr_;
139
140 62 crypto::CleanupLibcryptoMt();
141 62 }
142
143
144 //------------------------------------------------------------------------------
145
146
147 26 LibContext *LibContext::Create(const string &fqrn,
148 OptionsManager *options_mgr) {
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 assert(options_mgr != NULL);
150 26 LibContext *ctx = new LibContext();
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 assert(ctx != NULL);
152
153 26 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 26 times.
26 if (ctx->mount_point_->bundle_mgr() != nullptr) {
158 ctx->mount_point_->bundle_mgr()->Spawn();
159 }
160 26 return ctx;
161 }
162
163
164 26 LibContext::LibContext() : options_mgr_(NULL), mount_point_(NULL) { }
165
166
167 26 LibContext::~LibContext() {
168
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 delete mount_point_;
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 delete options_mgr_;
170 26 }
171
172 void LibContext::EnableMultiThreaded() {
173 mount_point_->download_mgr()->Spawn();
174 }
175
176 95 bool LibContext::GetDirentForPath(const PathString &path,
177 catalog::DirectoryEntry *dirent) {
178
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 95 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 95 times.
95 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 95 times.
✗ Branch 4 not taken.
95 const shash::Md5 md5path(path.GetChars(), path.GetLength());
184
3/4
✓ Branch 2 taken 95 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40 times.
✓ Branch 5 taken 55 times.
95 if (mount_point_->md5path_cache()->Lookup(md5path, dirent))
185 40 return dirent->GetSpecial() != catalog::kDirentNegative;
186
187 // TODO(jblomer): not twice md5 calculation
188
3/4
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
✓ Branch 5 taken 7 times.
55 if (mount_point_->catalog_mgr()->LookupPath(path, catalog::kLookupDefault,
189 dirent)) {
190
1/2
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 mount_point_->md5path_cache()->Insert(md5path, *dirent);
191 48 return true;
192 }
193
194
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry");
195 // Only cache real ENOENT errors, not catalog load errors
196
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 if (dirent->GetSpecial() == catalog::kDirentNegative)
197
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 mount_point_->md5path_cache()->InsertNegative(md5path);
198
199 7 return false;
200 }
201
202
203 48 void LibContext::AppendStringToList(char const *str,
204 char ***buf,
205 size_t *listlen,
206 size_t *buflen) {
207
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 36 times.
48 if (*listlen + 1 >= *buflen) {
208 12 const size_t newbuflen = (*listlen) * 2 + 5;
209 12 *buf = reinterpret_cast<char **>(realloc(*buf, sizeof(char *) * newbuflen));
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 assert(*buf);
211 12 *buflen = newbuflen;
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 assert(*listlen < *buflen);
213 }
214
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 9 times.
48 if (str) {
215 39 (*buf)[(*listlen)] = strdup(str);
216 // null-terminate the list
217 39 (*buf)[++(*listlen)] = NULL;
218 } else {
219 9 (*buf)[(*listlen)] = NULL;
220 }
221 48 }
222
223
224 12 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 3 times.
✓ Branch 1 taken 9 times.
12 if (*listlen + 1 >= *buflen) {
229 3 const size_t newbuflen = (*listlen) * 2 + 5;
230 3 *buf = reinterpret_cast<cvmfs_stat_t *>(
231 3 realloc(*buf, sizeof(cvmfs_stat_t) * newbuflen));
232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(*buf);
233 3 *buflen = newbuflen;
234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 assert(*listlen < *buflen);
235 }
236 12 (*buf)[(*listlen)].info = st.info;
237 12 (*buf)[(*listlen)++].name = st.name;
238 12 }
239
240 80 int LibContext::GetAttr(const char *c_path, struct stat *info) {
241
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 perf::Inc(file_system()->n_fs_stat());
242 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
243
1/2
✓ Branch 4 taken 80 times.
✗ Branch 5 not taken.
80 &default_interrupt_cue_);
244
245
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for path: %s", c_path);
246
247 80 PathString p;
248
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 p.Assign(c_path, strlen(c_path));
249
250
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 catalog::DirectoryEntry dirent;
251
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 const bool found = GetDirentForPath(p, &dirent);
252
253
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 76 times.
80 if (!found) {
254 4 return -ENOENT;
255 }
256
257
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 *info = dirent.GetStatStructure();
258 76 return 0;
259 80 }
260
261 6 void LibContext::CvmfsAttrFromDirent(const catalog::DirectoryEntry dirent,
262 struct cvmfs_attr *attr) {
263 6 attr->st_ino = dirent.inode();
264 6 attr->st_mode = dirent.mode();
265 6 attr->st_nlink = dirent.linkcount();
266 6 attr->st_uid = dirent.uid();
267 6 attr->st_gid = dirent.gid();
268 6 attr->st_rdev = dirent.rdev();
269 6 attr->st_size = dirent.size();
270 6 attr->mtime = dirent.mtime();
271
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 attr->cvm_checksum = strdup(dirent.checksum().ToString().c_str());
272 6 attr->cvm_symlink = strdup(dirent.symlink().c_str());
273 6 attr->cvm_name = strdup(dirent.name().c_str());
274 6 attr->cvm_xattrs = NULL;
275 6 }
276
277
278 9 int LibContext::GetExtAttr(const char *c_path, struct cvmfs_attr *info) {
279 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
280
1/2
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
9 &default_interrupt_cue_);
281
282
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for path: %s", c_path);
283
284 9 PathString p;
285
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 p.Assign(c_path, strlen(c_path));
286
287
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 catalog::DirectoryEntry dirent;
288
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 const bool found = GetDirentForPath(p, &dirent);
289
290
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 if (!found) {
291 3 return -ENOENT;
292 }
293
294
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 CvmfsAttrFromDirent(dirent, info);
295 // Chunked files without bulk hash need to be treated specially
296 6 info->cvm_nchunks = 0;
297 6 info->cvm_is_hash_artificial = 0;
298
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
6 if (dirent.IsRegular()) {
299 3 info->cvm_nchunks = 1;
300
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 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 6 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
6 info->cvm_parent = strdup(GetParentPath(c_path).c_str());
319
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (dirent.HasXattrs()) {
320 XattrList *xattrs = new XattrList();
321 mount_point_->catalog_mgr()->LookupXattrs(p, xattrs);
322 info->cvm_xattrs = xattrs;
323 }
324 6 return 0;
325 9 }
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 3 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 3 times.
✗ Branch 2 not taken.
3 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_listdir on path: %s", c_path);
363 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
364
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 &default_interrupt_cue_);
365
366
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (c_path[0] == '/' && c_path[1] == '\0') {
367 // root path is expected to be "", not "/"
368 c_path = "";
369 }
370
371 3 PathString path;
372
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 path.Assign(c_path, strlen(c_path));
373
374
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 catalog::DirectoryEntry d;
375
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const bool found = GetDirentForPath(path, &d);
376
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!found) {
378 return -ENOENT;
379 }
380
381
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!d.IsDirectory()) {
382 return -ENOTDIR;
383 }
384
385 3 AppendStringToList(NULL, buf, listlen, buflen);
386
387 // Build listing
388
389
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (self_reference) {
390 // Add current directory link
391 3 AppendStringToList(".", buf, listlen, buflen);
392
393 // Add parent directory link
394
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const catalog::DirectoryEntry p;
395
2/4
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 if (d.inode() != mount_point_->catalog_mgr()->GetRootInode()) {
396 3 AppendStringToList("..", buf, listlen, buflen);
397 }
398 3 }
399
400 // Add all names
401
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 catalog::StatEntryList listing_from_catalog;
402
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (!mount_point_->catalog_mgr()->ListingStat(path, &listing_from_catalog)) {
403 return -EIO;
404 }
405
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
9 for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
406 6 AppendStringToList(listing_from_catalog.AtPtr(i)->name.c_str(), buf,
407 listlen, buflen);
408 }
409
410 3 return 0;
411 3 }
412
413 3 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 3 times.
✗ Branch 2 not taken.
3 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_listdir_stat on path: %s", c_path);
418 const ClientCtxGuard ctxg(geteuid(), getegid(), getpid(),
419
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 &default_interrupt_cue_);
420
421
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (c_path[0] == '/' && c_path[1] == '\0') {
422 // root path is expected to be "", not "/"
423 c_path = "";
424 }
425
426 3 PathString path;
427
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 path.Assign(c_path, strlen(c_path));
428
429
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 catalog::DirectoryEntry d;
430
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 const bool found = GetDirentForPath(path, &d);
431
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!found) {
433 return -ENOENT;
434 }
435
436
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!d.IsDirectory()) {
437 return -ENOTDIR;
438 }
439
440 // Build listing
441
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 catalog::StatEntryList listing_from_catalog;
442
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (!mount_point_->catalog_mgr()->ListingStat(path, &listing_from_catalog)) {
443 return -EIO;
444 }
445
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 3 times.
15 for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
446 cvmfs_stat_t st;
447 12 st.info = listing_from_catalog.AtPtr(i)->info;
448 12 st.name = strdup(listing_from_catalog.AtPtr(i)->name.c_str());
449 12 AppendStatToList(st, buf, listlen, buflen);
450 }
451
452 3 return 0;
453 3 }
454
455 9 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 9 times.
✗ Branch 5 not taken.
9 &default_interrupt_cue_);
459
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_stat_nc (cvmfs_nc_attr) : %s", c_path);
460
461 9 PathString p;
462
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 p.Assign(c_path, strlen(c_path));
463
464 9 PathString mountpoint;
465
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 shash::Any hash;
466 uint64_t size;
467
468 // Find the nested catalog from the root catalog
469
1/2
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 const bool found = mount_point_->catalog_mgr()->LookupNested(p, &mountpoint,
470 &hash, &size);
471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (!found) {
472 return -ENOENT;
473 }
474
475 9 std::string subcat_path;
476
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 shash::Any tmp_hash;
477 9 std::map<std::string, uint64_t> counters = mount_point_->catalog_mgr()
478
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 ->LookupCounters(
479 p, &subcat_path, &tmp_hash)
480
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 .GetValues();
481
482 // Set values of the passed structure
483
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 nc_attr->mountpoint = strdup(mountpoint.ToString().c_str());
484
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 nc_attr->hash = strdup(hash.ToString().c_str());
485 9 nc_attr->size = size;
486
487
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_regular = counters["regular"];
488
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_symlink = counters["symlink"];
489
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_special = counters["special"];
490
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_dir = counters["dir"];
491
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_nested = counters["nested"];
492
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_chunked = counters["chunked"];
493
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_chunks = counters["chunks"];
494
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_file_size = counters["file_size"];
495
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_chunked_size = counters["chunked_size"];
496
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_xattr = counters["xattr"];
497
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_external = counters["external"];
498
2/4
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
9 nc_attr->ctr_external_file_size = counters["external_file_size"];
499 9 return 0;
500 9 }
501
502
503 6 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 6 times.
✗ Branch 5 not taken.
6 &default_interrupt_cue_);
508
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_list_nc on path: %s", c_path);
509
510
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (c_path[0] == '/' && c_path[1] == '\0') {
511 // root path is expected to be "", not "/"
512 c_path = "";
513 }
514
515 6 PathString path;
516
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 path.Assign(c_path, strlen(c_path));
517
518 6 std::vector<PathString> skein;
519
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 const bool retval = mount_point_->catalog_mgr()->ListCatalogSkein(path,
520 &skein);
521
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 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 6 size_t listlen = 0;
528 6 AppendStringToList(NULL, buf, &listlen, buflen);
529
530
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 6 times.
33 for (unsigned i = 0; i < skein.size(); i++) {
531
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 AppendStringToList(skein.at(i).c_str(), buf, &listlen, buflen);
532 }
533
534 6 return 0;
535 6 }
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