GCC Code Coverage Report


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