Directory: | cvmfs/ |
---|---|
File: | cvmfs/libcvmfs_int.cc |
Date: | 2025-04-20 02:34:28 |
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 | |||
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/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 |