GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/libcvmfs_int.cc Lines: 187 308 60.7 %
Date: 2019-02-03 02:48:13 Branches: 65 164 39.6 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 *
4
 * This is the internal implementation of libcvmfs, not to be exposed
5
 * to the code using the library.  This code is based heavily on the
6
 * fuse module cvmfs.cc.
7
 */
8
9
#define ENOATTR ENODATA  /**< instead of including attr/xattr.h */
10
11
#include <sys/xattr.h>
12
#include "cvmfs_config.h"
13
#include "libcvmfs_int.h"
14
15
#include <dirent.h>
16
#include <errno.h>
17
#include <fcntl.h>
18
#include <google/dense_hash_map>
19
#include <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
}