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 |