GCC Code Coverage Report


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