GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/libcvmfs_int.cc
Date: 2026-04-19 02:41:37
Exec Total Coverage
Lines: 229 390 58.7%
Branches: 159 490 32.4%

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