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