CernVM-FS  2.11.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
cvmfs.cc
Go to the documentation of this file.
1 
22 // TODO(jblomer): the file system root should probably always return 1 for an
23 // inode. See also integration test #23.
24 
25 #define ENOATTR ENODATA
27 #ifndef __STDC_FORMAT_MACROS
28 #define __STDC_FORMAT_MACROS
29 #endif
30 
31 // sys/xattr.h conflicts with linux/xattr.h and needs to be loaded very early
32 #include <sys/xattr.h> // NOLINT
33 
34 #include "cvmfs_config.h"
35 #include "cvmfs.h"
36 
37 #include <dirent.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <google/dense_hash_map>
41 #include <inttypes.h>
42 #include <pthread.h>
43 #include <stddef.h>
44 #include <stdint.h>
45 #include <sys/errno.h>
46 #include <sys/file.h>
47 #include <sys/mount.h>
48 #include <sys/resource.h>
49 #include <sys/stat.h>
50 #include <sys/time.h>
51 #include <sys/types.h>
52 #include <sys/wait.h>
53 #include <unistd.h>
54 
55 #include <algorithm>
56 #include <cassert>
57 #include <cstdio>
58 #include <cstdlib>
59 #include <cstring>
60 #include <ctime>
61 #include <functional>
62 #include <map>
63 #include <new>
64 #include <string>
65 #include <vector>
66 
67 #include "authz/authz_session.h"
68 #include "auto_umount.h"
69 #include "backoff.h"
70 #include "cache.h"
71 #include "catalog_mgr_client.h"
72 #include "clientctx.h"
73 #include "compat.h"
74 #include "compression.h"
75 #include "crypto/crypto_util.h"
76 #include "crypto/hash.h"
77 #include "crypto/signature.h"
78 #include "directory_entry.h"
79 #include "duplex_fuse.h"
80 #include "fence.h"
81 #include "fetch.h"
82 #include "file_chunk.h"
83 #include "fuse_inode_gen.h"
84 #include "fuse_remount.h"
85 #include "globals.h"
86 #include "glue_buffer.h"
87 #include "history_sqlite.h"
88 #include "interrupt.h"
89 #include "loader.h"
90 #include "lru_md.h"
91 #include "magic_xattr.h"
92 #include "manifest_fetch.h"
93 #include "monitor.h"
94 #include "mountpoint.h"
95 #include "network/download.h"
96 #include "nfs_maps.h"
97 #include "notification_client.h"
98 #include "options.h"
99 #include "quota_listener.h"
100 #include "quota_posix.h"
101 #include "shortstring.h"
102 #include "sqlitemem.h"
103 #include "sqlitevfs.h"
104 #include "statistics.h"
105 #include "talk.h"
106 #include "telemetry_aggregator.h"
107 #include "tracer.h"
108 #include "util/algorithm.h"
109 #include "util/atomic.h"
110 #include "util/concurrency.h"
111 #include "util/exception.h"
112 #include "util/logging.h"
113 #include "util/platform.h"
114 #include "util/smalloc.h"
115 #include "util/uuid.h"
116 #include "wpad.h"
117 #include "xattr.h"
118 
119 using namespace std; // NOLINT
120 
121 namespace cvmfs {
122 
130 
131 
137  char *buffer;
139  // Not really used anymore. But directory listing needs to be migrated during
140  // hotpatch. If buffer is allocated by smmap, capacity is zero.
141  size_t size;
142  size_t capacity;
143 
144  DirectoryListing() : buffer(NULL), size(0), capacity(0) { }
145 };
146 
149 pid_t pid_ = 0;
152 
153 
154 typedef google::dense_hash_map<uint64_t, DirectoryListing,
158 pthread_mutex_t lock_directory_handles_ = PTHREAD_MUTEX_INITIALIZER;
160 
161 unsigned max_open_files_;
165 const int kNumReservedFd = 512;
169 const unsigned int kMinOpenFiles = 8192;
170 
171 
173  public:
174  explicit FuseInterruptCue(fuse_req_t *r) : req_ptr_(r) { }
175  virtual ~FuseInterruptCue() { }
176  virtual bool IsCanceled() { return fuse_req_interrupted(*req_ptr_); }
177  private:
178  fuse_req_t *req_ptr_;
179 };
180 
181 
182 static inline double GetKcacheTimeout() {
183  if (!fuse_remounter_->IsCaching())
184  return 0.0;
186 }
187 
188 
189 void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode) {
190  *drainout_mode = fuse_remounter_->IsInDrainoutMode();
191  *maintenance_mode = fuse_remounter_->IsInMaintenanceMode();
192 }
193 
194 
195 static bool UseWatchdog() {
196  if (loader_exports_ == NULL || loader_exports_->version < 2) {
197  return true; // spawn watchdog by default
198  // Note: with library versions before 2.1.8 it might not
199  // create stack traces properly in all cases
200  }
201 
203 }
204 
205 std::string PrintInodeGeneration() {
206  return "init-catalog-revision: " +
208  "current-catalog-revision: " +
210  "incarnation: " + StringifyInt(inode_generation_info_.incarnation) + " " +
212  + "\n";
213 }
214 
215 
216 static bool CheckVoms(const fuse_ctx &fctx) {
218  return true;
219  string mreq = mount_point_->membership_req();
220  LogCvmfs(kLogCvmfs, kLogDebug, "Got VOMS authz %s from filesystem "
221  "properties", mreq.c_str());
222 
223  if (fctx.uid == 0)
224  return true;
225 
226  return mount_point_->authz_session_mgr()->IsMemberOf(fctx.pid, mreq);
227 }
228 
230  return dirent.IsRegular() &&
231  (dirent.inode() < mount_point_->catalog_mgr()->GetRootInode());
232 }
233 
235  const catalog::DirectoryEntry &dirent,
236  const shash::Any &hash,
237  const struct stat &info)
238 {
239  if (hash == dirent.checksum())
240  return false;
241  // For chunked files, we don't want to load the full list of chunk hashes
242  // so we only check the last modified timestamp
243  if (dirent.IsChunkedFile() && (info.st_mtime == dirent.mtime()))
244  return false;
245  return true;
246 }
247 
258 static bool FixupOpenInode(const PathString &path,
259  catalog::DirectoryEntry *dirent)
260 {
261  if (!MayBeInPageCacheTracker(*dirent))
262  return false;
263 
264  shash::Any hash_open;
265  struct stat info;
266  bool is_open = mount_point_->page_cache_tracker()->GetInfoIfOpen(
267  dirent->inode(), &hash_open, &info);
268  if (!is_open)
269  return false;
270  if (!HasDifferentContent(*dirent, hash_open, info))
271  return false;
272 
273  // Overwrite dirent with inode from current generation
274  bool found = mount_point_->catalog_mgr()->LookupPath(
275  path, catalog::kLookupDefault, dirent);
276  assert(found);
277 
278  return true;
279 }
280 
281 static bool GetDirentForInode(const fuse_ino_t ino,
282  catalog::DirectoryEntry *dirent)
283 {
284  // Lookup inode in cache
285  if (mount_point_->inode_cache()->Lookup(ino, dirent))
286  return true;
287 
288  // Look in the catalogs in 2 steps: lookup inode->path, lookup path
289  static catalog::DirectoryEntry dirent_negative =
291  // Reset directory entry. If the function returns false and dirent is no
292  // the kDirentNegative, it was an I/O error
293  *dirent = catalog::DirectoryEntry();
294 
296 
297  if (file_system_->IsNfsSource()) {
298  // NFS mode
299  PathString path;
300  bool retval = file_system_->nfs_maps()->GetPath(ino, &path);
301  if (!retval) {
302  *dirent = dirent_negative;
303  return false;
304  }
305  if (catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent)) {
306  // Fix inodes
307  dirent->set_inode(ino);
308  mount_point_->inode_cache()->Insert(ino, *dirent);
309  return true;
310  }
311  return false; // Not found in catalog or catalog load error
312  }
313 
314  // Non-NFS mode
315  PathString path;
316  if (ino == catalog_mgr->GetRootInode()) {
317  bool retval =
318  catalog_mgr->LookupPath(PathString(), catalog::kLookupDefault, dirent);
319 
321  "GetDirentForInode: Race condition? Not found dirent %s",
322  dirent->name().c_str())) {
323  return false;
324  }
325 
326  dirent->set_inode(ino);
327  mount_point_->inode_cache()->Insert(ino, *dirent);
328  return true;
329  }
330 
332  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, &path);
333  if (!retval) {
334  // This may be a retired inode whose stat information is only available
335  // in the page cache tracker because there is still an open file
337  "GetDirentForInode inode lookup failure %" PRId64, ino);
338  *dirent = dirent_negative;
339  // Indicate that the inode was not found in the tracker rather than not
340  // found in the catalog
341  dirent->set_inode(ino);
342  return false;
343  }
344  if (catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent)) {
345  if (!inode_ex.IsCompatibleFileType(dirent->mode())) {
347  "Warning: inode %" PRId64 " (%s) changed file type",
348  ino, path.c_str());
349  // TODO(jblomer): we detect this issue but let it continue unhandled.
350  // Fix me.
351  }
352 
353  // Fix inodes
354  dirent->set_inode(ino);
355  mount_point_->inode_cache()->Insert(ino, *dirent);
356  return true;
357  }
358 
359  // Can happen after reload of catalogs or on catalog load failure
360  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForInode path lookup failure");
361  return false;
362 }
363 
364 
372 static uint64_t GetDirentForPath(const PathString &path,
373  catalog::DirectoryEntry *dirent)
374 {
375  uint64_t live_inode = 0;
376  if (!file_system_->IsNfsSource())
377  live_inode = mount_point_->inode_tracker()->FindInode(path);
378 
379  shash::Md5 md5path(path.GetChars(), path.GetLength());
380  if (mount_point_->md5path_cache()->Lookup(md5path, dirent)) {
381  if (dirent->GetSpecial() == catalog::kDirentNegative)
382  return false;
383  // We may have initially stored the entry with an old inode in the
384  // md5path cache and now should update it with the new one.
385  if (!file_system_->IsNfsSource() && (live_inode != 0))
386  dirent->set_inode(live_inode);
387  return 1;
388  }
389 
391 
392  // Lookup inode in catalog TODO: not twice md5 calculation
393  bool retval;
394  retval = catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent);
395  if (retval) {
396  if (file_system_->IsNfsSource()) {
397  dirent->set_inode(file_system_->nfs_maps()->GetInode(path));
398  mount_point_->md5path_cache()->Insert(md5path, *dirent);
399  } else if (live_inode != 0) {
400  dirent->set_inode(live_inode);
401  if (FixupOpenInode(path, dirent)) {
403  "content of %s change, replacing inode %" PRIu64 " --> %" PRIu64,
404  path.c_str(), live_inode, dirent->inode());
405  return live_inode;
406  // Do not populate the md5path cache until the inode tracker is fixed
407  } else {
408  mount_point_->md5path_cache()->Insert(md5path, *dirent);
409  }
410  }
411  return 1;
412  }
413 
414  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry");
415  // Only insert ENOENT results into negative cache. Otherwise it was an
416  // error loading nested catalogs
417  if (dirent->GetSpecial() == catalog::kDirentNegative)
419  return 0;
420 }
421 
422 
423 static bool GetPathForInode(const fuse_ino_t ino, PathString *path) {
424  // Check the path cache first
425  if (mount_point_->path_cache()->Lookup(ino, path))
426  return true;
427 
428  if (file_system_->IsNfsSource()) {
429  // NFS mode, just a lookup
430  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - lookup in NFS maps", ino);
431  if (file_system_->nfs_maps()->GetPath(ino, path)) {
432  mount_point_->path_cache()->Insert(ino, *path);
433  return true;
434  }
435  return false;
436  }
437 
438  if (ino == mount_point_->catalog_mgr()->GetRootInode())
439  return true;
440 
441  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - looking in inode tracker", ino);
443  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, path);
444 
446  "GetPathForInode: Race condition? "
447  "Inode not found in inode tracker at path %s",
448  path->c_str())) {
449  return false;
450  }
451 
452 
453  mount_point_->path_cache()->Insert(ino, *path);
454  return true;
455 }
456 
457 static void DoTraceInode(const int event,
458  fuse_ino_t ino,
459  const std::string &msg)
460 {
461  PathString path;
462  bool found = GetPathForInode(ino, &path);
463  if (!found) {
465  "Tracing: Could not find path for inode %" PRIu64, uint64_t(ino));
466  mount_point_->tracer()->Trace(event, PathString("@UNKNOWN"), msg);
467  } else {
468  mount_point_->tracer()->Trace(event, path, msg);
469  }
470 }
471 
472 static void inline TraceInode(const int event,
473  fuse_ino_t ino,
474  const std::string &msg)
475 {
476  if (mount_point_->tracer()->IsActive()) DoTraceInode(event, ino, msg);
477 }
478 
484 static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
486 
488  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
489  FuseInterruptCue ic(&req);
490  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
492 
495 
496  fuse_ino_t parent_fuse = parent;
497  parent = catalog_mgr->MangleInode(parent);
499  "cvmfs_lookup in parent inode: %" PRIu64 " for name: %s",
500  uint64_t(parent), name);
501 
502  PathString path;
503  PathString parent_path;
504  uint64_t live_inode = 0;
506  struct fuse_entry_param result;
507 
508  memset(&result, 0, sizeof(result));
509  double timeout = GetKcacheTimeout();
510  result.attr_timeout = timeout;
511  result.entry_timeout = timeout;
512 
513  // Special NFS lookups: . and ..
514  if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
515  if (GetDirentForInode(parent, &dirent)) {
516  if (strcmp(name, ".") == 0) {
517  goto lookup_reply_positive;
518  } else {
519  // Lookup for ".."
520  if (dirent.inode() == catalog_mgr->GetRootInode()) {
521  dirent.set_inode(1);
522  goto lookup_reply_positive;
523  }
524  if (!GetPathForInode(parent, &parent_path))
525  goto lookup_reply_negative;
526  if (GetDirentForPath(GetParentPath(parent_path), &dirent) > 0)
527  goto lookup_reply_positive;
528  }
529  }
530  // No entry for "." or no entry for ".."
531  if (dirent.GetSpecial() == catalog::kDirentNegative)
532  goto lookup_reply_negative;
533  else
534  goto lookup_reply_error;
535  assert(false);
536  }
537 
538  if (!GetPathForInode(parent, &parent_path)) {
539  LogCvmfs(kLogCvmfs, kLogDebug, "no path for parent inode found");
540  goto lookup_reply_negative;
541  }
542 
543  path.Assign(parent_path);
544  path.Append("/", 1);
545  path.Append(name, strlen(name));
546  mount_point_->tracer()->Trace(Tracer::kEventLookup, path, "lookup()");
547  live_inode = GetDirentForPath(path, &dirent);
548  if (live_inode == 0) {
549  if (dirent.GetSpecial() == catalog::kDirentNegative)
550  goto lookup_reply_negative;
551  else
552  goto lookup_reply_error;
553  }
554 
555  lookup_reply_positive:
556  if (!file_system_->IsNfsSource()) {
557  if (live_inode > 1) {
558  // live inode is stale (open file), we replace it
559  assert(dirent.IsRegular());
560  assert(dirent.inode() != live_inode);
561  // The new inode is put in the tracker with refcounter == 0
562  bool replaced = mount_point_->inode_tracker()->ReplaceInode(
563  live_inode, glue::InodeEx(dirent.inode(), dirent.mode()));
564  if (replaced)
566  }
568  glue::InodeEx(dirent.inode(), dirent.mode()), path);
569  }
570  // We do _not_ track (and evict) positive replies; among other things, test
571  // 076 fails with the following line uncommented
572  //
573  // WARNING! ENABLING THIS BREAKS ANY TYPE OF MOUNTPOINT POINTING TO THIS INODE
574  //
575  // only safe if fuse_expire_entry is available
577  || (mount_point_->cache_symlinks() && dirent.IsLink())) {
578  LogCvmfs(kLogCache, kLogDebug, "Dentry to evict: %s", name);
579  mount_point_->dentry_tracker()->Add(parent_fuse, name,
580  static_cast<uint64_t>(timeout));
581  }
582 
584  result.ino = dirent.inode();
585  result.attr = dirent.GetStatStructure();
586  fuse_reply_entry(req, &result);
587  return;
588 
589  lookup_reply_negative:
590  // Will be a no-op if there is no fuse cache eviction
591  mount_point_->dentry_tracker()->Add(parent_fuse, name, uint64_t(timeout));
594  result.ino = 0;
595  fuse_reply_entry(req, &result);
596  return;
597 
598  lookup_reply_error:
600 
601  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, "EIO (01) on %s", name);
604 
605  fuse_reply_err(req, EIO);
606 }
607 
608 
612 static void cvmfs_forget(
613  fuse_req_t req,
614  fuse_ino_t ino,
615 #if CVMFS_USE_LIBFUSE == 2
616  unsigned long nlookup // NOLINT
617 #else
618  uint64_t nlookup
619 #endif
620 ) {
622 
624 
625  // The libfuse high-level library does the same
626  if (ino == FUSE_ROOT_ID) {
627  fuse_reply_none(req);
628  return;
629  }
630 
632  ino = mount_point_->catalog_mgr()->MangleInode(ino);
633  // This has been seen to deadlock on the debug log mutex on SL5. Problem of
634  // old kernel/fuse?
635 #if CVMFS_USE_LIBCVMFS == 2
636  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %u",
637  uint64_t(ino), nlookup);
638 #else
639  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
640  uint64_t(ino), nlookup);
641 #endif
642  if (!file_system_->IsNfsSource()) {
643  bool removed =
644  mount_point_->inode_tracker()->GetVfsPutRaii().VfsPut(ino, nlookup);
645  if (removed)
647  }
649  fuse_reply_none(req);
650 }
651 
652 
653 #if (FUSE_VERSION >= 29)
654 static void cvmfs_forget_multi(
655  fuse_req_t req,
656  size_t count,
657  struct fuse_forget_data *forgets
658 ) {
660 
662  if (file_system_->IsNfsSource()) {
663  fuse_reply_none(req);
664  return;
665  }
666 
668  {
669  glue::InodeTracker::VfsPutRaii vfs_put_raii =
673  for (size_t i = 0; i < count; ++i) {
674  if (forgets[i].ino == FUSE_ROOT_ID) {
675  continue;
676  }
677 
678  uint64_t ino = mount_point_->catalog_mgr()->MangleInode(forgets[i].ino);
679  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
680  ino, forgets[i].nlookup);
681 
682  bool removed = vfs_put_raii.VfsPut(ino, forgets[i].nlookup);
683  if (removed)
684  evict_raii.Evict(ino);
685  }
686  }
688 
689  fuse_reply_none(req);
690 }
691 #endif // FUSE_VERSION >= 29
692 
693 
700 static void ReplyNegative(const catalog::DirectoryEntry &dirent,
701  fuse_req_t req)
702 {
703  if (dirent.GetSpecial() == catalog::kDirentNegative) {
704  fuse_reply_err(req, ENOENT);
705  } else {
706  const char * name = dirent.name().c_str();
707  const char * link = dirent.symlink().c_str();
708 
710  "EIO (02) name=%s symlink=%s",
711  name ? name: "<unset>",
712  link ? link: "<unset>");
713 
716  fuse_reply_err(req, EIO);
717  }
718 }
719 
720 
724 static void cvmfs_getattr(fuse_req_t req, fuse_ino_t ino,
725  struct fuse_file_info *fi)
726 {
728 
730  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
731  FuseInterruptCue ic(&req);
732  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
734 
736  ino = mount_point_->catalog_mgr()->MangleInode(ino);
737  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for inode: %" PRIu64,
738  uint64_t(ino));
739 
740  if (!CheckVoms(*fuse_ctx)) {
742  fuse_reply_err(req, EACCES);
743  return;
744  }
746  bool found = GetDirentForInode(ino, &dirent);
747  TraceInode(Tracer::kEventGetAttr, ino, "getattr()");
748  if ((!found && (dirent.inode() == ino)) || MayBeInPageCacheTracker(dirent)) {
749  // Serve retired inode from page cache tracker; even if we find it in the
750  // catalog, we replace the dirent by the page cache tracker version to
751  // not confuse open file handles
752  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr %" PRIu64 " "
753  "served from page cache tracker", ino);
754  shash::Any hash;
755  struct stat info;
756  bool is_open =
757  mount_point_->page_cache_tracker()->GetInfoIfOpen(ino, &hash, &info);
758  if (is_open) {
760  if (found && HasDifferentContent(dirent, hash, info)) {
761  // We should from now on provide the new inode information instead
762  // of the stale one. To this end, we need to invalidate the dentry to
763  // trigger a fresh LOOKUP call
764  uint64_t parent_ino;
765  NameString name;
767  dirent.inode(), &parent_ino, &name))
768  {
769  fuse_remounter_->InvalidateDentry(parent_ino, name);
770  }
772  }
773  fuse_reply_attr(req, &info, GetKcacheTimeout());
774  return;
775  }
776  }
778 
779  if (!found) {
780  ReplyNegative(dirent, req);
781  return;
782  }
783 
784  struct stat info = dirent.GetStatStructure();
785 
786  fuse_reply_attr(req, &info, GetKcacheTimeout());
787 }
788 
789 
793 static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino) {
795 
797  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
798  FuseInterruptCue ic(&req);
799  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
800 
802  ino = mount_point_->catalog_mgr()->MangleInode(ino);
803  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_readlink on inode: %" PRIu64,
804  uint64_t(ino));
805 
807  const bool found = GetDirentForInode(ino, &dirent);
808  TraceInode(Tracer::kEventReadlink, ino, "readlink()");
810 
811  if (!found) {
812  ReplyNegative(dirent, req);
813  return;
814  }
815 
816  if (!dirent.IsLink()) {
817  fuse_reply_err(req, EINVAL);
818  return;
819  }
820 
821  fuse_reply_readlink(req, dirent.symlink().c_str());
822 }
823 
824 
825 static void AddToDirListing(const fuse_req_t req,
826  const char *name, const struct stat *stat_info,
827  BigVector<char> *listing)
828 {
829  LogCvmfs(kLogCvmfs, kLogDebug, "Add to listing: %s, inode %" PRIu64,
830  name, uint64_t(stat_info->st_ino));
831  size_t remaining_size = listing->capacity() - listing->size();
832  const size_t entry_size = fuse_add_direntry(req, NULL, 0, name, stat_info, 0);
833 
834  while (entry_size > remaining_size) {
835  listing->DoubleCapacity();
836  remaining_size = listing->capacity() - listing->size();
837  }
838 
839  char *buffer;
840  bool large_alloc;
841  listing->ShareBuffer(&buffer, &large_alloc);
842  fuse_add_direntry(req, buffer + listing->size(),
843  remaining_size, name, stat_info,
844  listing->size() + entry_size);
845  listing->SetSize(listing->size() + entry_size);
846 }
847 
848 
852 static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino,
853  struct fuse_file_info *fi)
854 {
856 
857  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
858  FuseInterruptCue ic(&req);
859  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
861 
864  ino = catalog_mgr->MangleInode(ino);
865  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64,
866  uint64_t(ino));
867  if (!CheckVoms(*fuse_ctx)) {
869  fuse_reply_err(req, EACCES);
870  return;
871  }
872 
873  TraceInode(Tracer::kEventOpenDir, ino, "opendir()");
874  PathString path;
876  bool found = GetPathForInode(ino, &path);
877  if (!found) {
879  fuse_reply_err(req, ENOENT);
880  return;
881  }
882  found = GetDirentForInode(ino, &d);
883 
884  if (!found) {
886  ReplyNegative(d, req);
887  return;
888  }
889  if (!d.IsDirectory()) {
891  fuse_reply_err(req, ENOTDIR);
892  return;
893  }
894 
895  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64 ", path %s",
896  uint64_t(ino), path.c_str());
897 
898  // Build listing
899  BigVector<char> fuse_listing(512);
900 
901  // Add current directory link
902  struct stat info;
903  info = d.GetStatStructure();
904  AddToDirListing(req, ".", &info, &fuse_listing);
905 
906  // Add parent directory link
908  if (d.inode() != catalog_mgr->GetRootInode() &&
909  (GetDirentForPath(GetParentPath(path), &p) > 0))
910  {
911  info = p.GetStatStructure();
912  AddToDirListing(req, "..", &info, &fuse_listing);
913  }
914 
915  // Add all names
916  catalog::StatEntryList listing_from_catalog;
917  bool retval = catalog_mgr->ListingStat(path, &listing_from_catalog);
918 
919  if (!retval) {
921  fuse_listing.Clear(); // Buffer is shared, empty manually
922 
924  "EIO (03) on %s", path.c_str());
927  fuse_reply_err(req, EIO);
928  return;
929  }
930  for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
931  // Fix inodes
932  PathString entry_path;
933  entry_path.Assign(path);
934  entry_path.Append("/", 1);
935  entry_path.Append(listing_from_catalog.AtPtr(i)->name.GetChars(),
936  listing_from_catalog.AtPtr(i)->name.GetLength());
937 
938  catalog::DirectoryEntry entry_dirent;
939  if (!GetDirentForPath(entry_path, &entry_dirent)) {
940  LogCvmfs(kLogCvmfs, kLogDebug, "listing entry %s vanished, skipping",
941  entry_path.c_str());
942  continue;
943  }
944 
945  struct stat fixed_info = listing_from_catalog.AtPtr(i)->info;
946  fixed_info.st_ino = entry_dirent.inode();
947  AddToDirListing(req, listing_from_catalog.AtPtr(i)->name.c_str(),
948  &fixed_info, &fuse_listing);
949  }
951 
952  DirectoryListing stream_listing;
953  stream_listing.size = fuse_listing.size();
954  stream_listing.capacity = fuse_listing.capacity();
955  bool large_alloc;
956  fuse_listing.ShareBuffer(&stream_listing.buffer, &large_alloc);
957  if (large_alloc)
958  stream_listing.capacity = 0;
959 
960  // Save the directory listing and return a handle to the listing
961  {
964  "linking directory handle %d to dir inode: %" PRIu64,
965  next_directory_handle_, uint64_t(ino));
966  (*directory_handles_)[next_directory_handle_] = stream_listing;
967  fi->fh = next_directory_handle_;
969  }
972 
973 #if (FUSE_VERSION >= 30)
974 #ifdef CVMFS_ENABLE_FUSE3_CACHE_READDIR
975  // This affects only reads on the same open directory handle (e.g. multiple
976  // reads with rewinddir() between them). A new opendir on the same directory
977  // will trigger readdir calls independently of this setting.
978  fi->cache_readdir = 1;
979 #endif
980 #endif
981  fuse_reply_open(req, fi);
982 }
983 
984 
988 static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino,
989  struct fuse_file_info *fi)
990 {
992 
993  ino = mount_point_->catalog_mgr()->MangleInode(ino);
994  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_releasedir on inode %" PRIu64
995  ", handle %d", uint64_t(ino), fi->fh);
996 
997  int reply = 0;
998 
999  {
1001  DirectoryHandles::iterator iter_handle = directory_handles_->find(fi->fh);
1002  if (iter_handle != directory_handles_->end()) {
1003  if (iter_handle->second.capacity == 0)
1004  smunmap(iter_handle->second.buffer);
1005  else
1006  free(iter_handle->second.buffer);
1007  directory_handles_->erase(iter_handle);
1009  } else {
1010  reply = EINVAL;
1011  }
1012  }
1013 
1014  fuse_reply_err(req, reply);
1015 }
1016 
1017 
1021 static void ReplyBufferSlice(const fuse_req_t req, const char *buffer,
1022  const size_t buffer_size, const off_t offset,
1023  const size_t max_size)
1024 {
1025  if (offset < static_cast<int>(buffer_size)) {
1026  fuse_reply_buf(req, buffer + offset,
1027  std::min(static_cast<size_t>(buffer_size - offset), max_size));
1028  } else {
1029  fuse_reply_buf(req, NULL, 0);
1030  }
1031 }
1032 
1033 
1037 static void cvmfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
1038  off_t off, struct fuse_file_info *fi)
1039 {
1041 
1043  "cvmfs_readdir on inode %" PRIu64 " reading %d bytes from offset %d",
1044  uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)), size, off);
1045 
1046  DirectoryListing listing;
1047 
1049  DirectoryHandles::const_iterator iter_handle =
1050  directory_handles_->find(fi->fh);
1051  if (iter_handle != directory_handles_->end()) {
1052  listing = iter_handle->second;
1053 
1054  ReplyBufferSlice(req, listing.buffer, listing.size, off, size);
1055  return;
1056  }
1057 
1058  fuse_reply_err(req, EINVAL);
1059 }
1060 
1062  struct fuse_file_info *fi)
1063 {
1065  fi->keep_cache = od.keep_cache;
1066  fi->direct_io = od.direct_io;
1067  if (fi->direct_io)
1069 }
1070 
1071 
1072 #ifdef __APPLE__
1073 // On macOS, xattr on a symlink opens and closes the file (with O_SYMLINK)
1074 // around the actual getxattr call. In order to not run into an I/O error
1075 // we use a special file handle for symlinks, from which one cannot read.
1076 static const uint64_t kFileHandleIgnore = static_cast<uint64_t>(2) << 60;
1077 #endif
1078 
1085 static void cvmfs_open(fuse_req_t req, fuse_ino_t ino,
1086  struct fuse_file_info *fi)
1087 {
1089 
1090  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1091  FuseInterruptCue ic(&req);
1092  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1093  fuse_remounter_->fence()->Enter();
1095  ino = catalog_mgr->MangleInode(ino);
1096  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_open on inode: %" PRIu64,
1097  uint64_t(ino));
1098 
1099  int fd = -1;
1100  catalog::DirectoryEntry dirent;
1101  PathString path;
1102 
1103  bool found = GetPathForInode(ino, &path);
1104  if (!found) {
1105  fuse_remounter_->fence()->Leave();
1106  fuse_reply_err(req, ENOENT);
1107  return;
1108  }
1109  found = GetDirentForInode(ino, &dirent);
1110  if (!found) {
1111  fuse_remounter_->fence()->Leave();
1112  ReplyNegative(dirent, req);
1113  return;
1114  }
1115 
1116  if (!CheckVoms(*fuse_ctx)) {
1117  fuse_remounter_->fence()->Leave();
1118  fuse_reply_err(req, EACCES);
1119  return;
1120  }
1121 
1122  mount_point_->tracer()->Trace(Tracer::kEventOpen, path, "open()");
1123  // Don't check. Either done by the OS or one wants to purposefully work
1124  // around wrong open flags
1125  // if ((fi->flags & 3) != O_RDONLY) {
1126  // fuse_reply_err(req, EROFS);
1127  // return;
1128  // }
1129 #ifdef __APPLE__
1130  if ((fi->flags & O_SHLOCK) || (fi->flags & O_EXLOCK)) {
1131  fuse_remounter_->fence()->Leave();
1132  fuse_reply_err(req, EOPNOTSUPP);
1133  return;
1134  }
1135  if (fi->flags & O_SYMLINK) {
1136  fuse_remounter_->fence()->Leave();
1137  fi->fh = kFileHandleIgnore;
1138  fuse_reply_open(req, fi);
1139  return;
1140  }
1141 #endif
1142  if (fi->flags & O_EXCL) {
1143  fuse_remounter_->fence()->Leave();
1144  fuse_reply_err(req, EEXIST);
1145  return;
1146  }
1147 
1148  perf::Inc(file_system_->n_fs_open()); // Count actual open / fetch operations
1149 
1151  if (!dirent.IsChunkedFile()) {
1152  if (dirent.IsDirectIo()) {
1153  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1154  } else {
1155  open_directives =
1157  ino, dirent.checksum(), dirent.GetStatStructure());
1158  }
1159  fuse_remounter_->fence()->Leave();
1160  } else {
1162  "chunked file %s opened (download delayed to read() call)",
1163  path.c_str());
1164 
1165  if (perf::Xadd(file_system_->no_open_files(), 1) >=
1166  (static_cast<int>(max_open_files_))-kNumReservedFd)
1167  {
1169  fuse_remounter_->fence()->Leave();
1170  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1171  fuse_reply_err(req, EMFILE);
1172  return;
1173  }
1174 
1175  // Figure out unique inode from annotated catalog
1176  // TODO(jblomer): we only need to lookup if the inode is not from the
1177  // current generation
1178  catalog::DirectoryEntry dirent_origin;
1179  if (!catalog_mgr->LookupPath(path, catalog::kLookupDefault,
1180  &dirent_origin)) {
1181  fuse_remounter_->fence()->Leave();
1183  "chunked file %s vanished unexpectedly", path.c_str());
1184  fuse_reply_err(req, ENOENT);
1185  return;
1186  }
1187  const uint64_t unique_inode = dirent_origin.inode();
1188 
1189  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1190  chunk_tables->Lock();
1191  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1192  chunk_tables->Unlock();
1193 
1194  // Retrieve File chunks from the catalog
1196  if (!catalog_mgr->ListFileChunks(path, dirent.hash_algorithm(),
1197  chunks.weak_ref()) ||
1198  chunks->IsEmpty())
1199  {
1200  fuse_remounter_->fence()->Leave();
1202  "EIO (04) file %s is marked as 'chunked', but no chunks found.",
1203  path.c_str());
1206  fuse_reply_err(req, EIO);
1207  return;
1208  }
1209  fuse_remounter_->fence()->Leave();
1210 
1211  chunk_tables->Lock();
1212  // Check again to avoid race
1213  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1214  chunk_tables->inode2chunks.Insert(
1215  unique_inode, FileChunkReflist(chunks.Release(), path,
1216  dirent.compression_algorithm(),
1217  dirent.IsExternalFile()));
1218  chunk_tables->inode2references.Insert(unique_inode, 1);
1219  } else {
1220  uint32_t refctr;
1221  bool retval =
1222  chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1223  assert(retval);
1224  chunk_tables->inode2references.Insert(unique_inode, refctr+1);
1225  }
1226  } else {
1227  fuse_remounter_->fence()->Leave();
1228  uint32_t refctr;
1229  bool retval =
1230  chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1231  assert(retval);
1232  chunk_tables->inode2references.Insert(unique_inode, refctr+1);
1233  }
1234 
1235  // Update the chunk handle list
1237  "linking chunk handle %d to unique inode: %" PRIu64,
1238  chunk_tables->next_handle, uint64_t(unique_inode));
1239  chunk_tables->handle2fd.Insert(chunk_tables->next_handle, ChunkFd());
1240  chunk_tables->handle2uniqino.Insert(chunk_tables->next_handle,
1241  unique_inode);
1242 
1243  // Generate artificial content hash as hash over chunk hashes
1244  // TODO(jblomer): we may want to cache the result in the chunk tables
1245  FileChunkReflist chunk_reflist;
1246  bool retval =
1247  chunk_tables->inode2chunks.Lookup(unique_inode, &chunk_reflist);
1248  assert(retval);
1249 
1250  fi->fh = chunk_tables->next_handle;
1251  if (dirent.IsDirectIo()) {
1252  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1253  } else {
1254  open_directives = mount_point_->page_cache_tracker()->Open(
1255  ino, chunk_reflist.HashChunkList(), dirent.GetStatStructure());
1256  }
1257  FillOpenFlags(open_directives, fi);
1258  fi->fh = static_cast<uint64_t>(-static_cast<int64_t>(fi->fh));
1259  ++chunk_tables->next_handle;
1260  chunk_tables->Unlock();
1261 
1262  fuse_reply_open(req, fi);
1263  return;
1264  }
1265 
1266  Fetcher *this_fetcher = dirent.IsExternalFile()
1268  : mount_point_->fetcher();
1269  fd = this_fetcher->Fetch(
1270  dirent.checksum(),
1271  dirent.size(),
1272  string(path.GetChars(), path.GetLength()),
1273  dirent.compression_algorithm(),
1277 
1278  if (fd >= 0) {
1280  (static_cast<int>(max_open_files_))-kNumReservedFd) {
1281  LogCvmfs(kLogCvmfs, kLogDebug, "file %s opened (fd %d)",
1282  path.c_str(), fd);
1283  fi->fh = fd;
1284  FillOpenFlags(open_directives, fi);
1285  fuse_reply_open(req, fi);
1286  return;
1287  } else {
1288  if (file_system_->cache_mgr()->Close(fd) == 0)
1290  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1291  fuse_reply_err(req, EMFILE);
1292  return;
1293  }
1294  assert(false);
1295  }
1296 
1297  // fd < 0
1299  "failed to open inode: %" PRIu64 ", CAS key %s, error code %d",
1300  uint64_t(ino), dirent.checksum().ToString().c_str(), errno);
1301  if (errno == EMFILE) {
1302  fuse_reply_err(req, EMFILE);
1303  return;
1304  }
1305 
1307 
1309  if (EIO == errno || EIO == -fd) {
1311  "EIO (06) on %s", path.c_str() );
1314  }
1315 
1316  fuse_reply_err(req, -fd);
1317 }
1318 
1319 
1323 static void cvmfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1324  struct fuse_file_info *fi)
1325 {
1327 
1329  "cvmfs_read inode: %" PRIu64 " reading %d bytes from offset %d "
1330  "fd %d", uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)),
1331  size, off, fi->fh);
1333 
1334 #ifdef __APPLE__
1335  if (fi->fh == kFileHandleIgnore) {
1336  fuse_reply_err(req, EBADF);
1337  return;
1338  }
1339 #endif
1340 
1341  // Get data chunk (<=128k guaranteed by Fuse)
1342  char *data = static_cast<char *>(alloca(size));
1343  unsigned int overall_bytes_fetched = 0;
1344 
1345  int64_t fd = static_cast<int64_t>(fi->fh);
1346  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1348 
1349  // Do we have a a chunked file?
1350  if (fd < 0) {
1351  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1352  FuseInterruptCue ic(&req);
1353  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1354 
1355  const uint64_t chunk_handle = abs_fd;
1356  uint64_t unique_inode;
1357  ChunkFd chunk_fd;
1358  FileChunkReflist chunks;
1359  bool retval;
1360 
1361  // Fetch unique inode, chunk list and file descriptor
1362  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1363  chunk_tables->Lock();
1364  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1365  if (!retval) {
1366  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1367  unique_inode = ino;
1368  }
1369  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &chunks);
1370  assert(retval);
1371  chunk_tables->Unlock();
1372 
1373  unsigned chunk_idx = chunks.FindChunkIdx(off);
1374 
1375  // Lock chunk handle
1376  pthread_mutex_t *handle_lock = chunk_tables->Handle2Lock(chunk_handle);
1377  MutexLockGuard m(handle_lock);
1378  chunk_tables->Lock();
1379  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1380  assert(retval);
1381  chunk_tables->Unlock();
1382 
1383  // Fetch all needed chunks and read the requested data
1384  off_t offset_in_chunk = off - chunks.list->AtPtr(chunk_idx)->offset();
1385  do {
1386  // Open file descriptor to chunk
1387  if ((chunk_fd.fd == -1) || (chunk_fd.chunk_idx != chunk_idx)) {
1388  if (chunk_fd.fd != -1) file_system_->cache_mgr()->Close(chunk_fd.fd);
1389  string verbose_path = "Part of " + chunks.path.ToString();
1390  if (chunks.external_data) {
1391  chunk_fd.fd = mount_point_->external_fetcher()->Fetch(
1392  chunks.list->AtPtr(chunk_idx)->content_hash(),
1393  chunks.list->AtPtr(chunk_idx)->size(),
1394  verbose_path,
1395  chunks.compression_alg,
1399  chunks.path.ToString(),
1400  chunks.list->AtPtr(chunk_idx)->offset());
1401  } else {
1402  chunk_fd.fd = mount_point_->fetcher()->Fetch(
1403  chunks.list->AtPtr(chunk_idx)->content_hash(),
1404  chunks.list->AtPtr(chunk_idx)->size(),
1405  verbose_path,
1406  chunks.compression_alg,
1410  }
1411  if (chunk_fd.fd < 0) {
1412  chunk_fd.fd = -1;
1413  chunk_tables->Lock();
1414  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1415  chunk_tables->Unlock();
1416 
1418  "EIO (05) on %s", chunks.path.ToString().c_str() );
1421  fuse_reply_err(req, EIO);
1422  return;
1423  }
1424  chunk_fd.chunk_idx = chunk_idx;
1425  }
1426 
1427  LogCvmfs(kLogCvmfs, kLogDebug, "reading from chunk fd %d",
1428  chunk_fd.fd);
1429  // Read data from chunk
1430  const size_t bytes_to_read = size - overall_bytes_fetched;
1431  const size_t remaining_bytes_in_chunk =
1432  chunks.list->AtPtr(chunk_idx)->size() - offset_in_chunk;
1433  size_t bytes_to_read_in_chunk =
1434  std::min(bytes_to_read, remaining_bytes_in_chunk);
1435  const int64_t bytes_fetched = file_system_->cache_mgr()->Pread(
1436  chunk_fd.fd,
1437  data + overall_bytes_fetched,
1438  bytes_to_read_in_chunk,
1439  offset_in_chunk);
1440 
1441  if (bytes_fetched < 0) {
1442  LogCvmfs(kLogCvmfs, kLogSyslogErr, "read err no %" PRId64 " (%s)",
1443  bytes_fetched, chunks.path.ToString().c_str());
1444  chunk_tables->Lock();
1445  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1446  chunk_tables->Unlock();
1447  if ( EIO == errno || EIO == -bytes_fetched ) {
1449  "EIO (07) on %s", chunks.path.ToString().c_str() );
1452  }
1453  fuse_reply_err(req, -bytes_fetched);
1454  return;
1455  }
1456  overall_bytes_fetched += bytes_fetched;
1457 
1458  // Proceed to the next chunk to keep on reading data
1459  ++chunk_idx;
1460  offset_in_chunk = 0;
1461  } while ((overall_bytes_fetched < size) &&
1462  (chunk_idx < chunks.list->size()));
1463 
1464  // Update chunk file descriptor
1465  chunk_tables->Lock();
1466  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1467  chunk_tables->Unlock();
1468  LogCvmfs(kLogCvmfs, kLogDebug, "released chunk file descriptor %d",
1469  chunk_fd.fd);
1470  } else {
1471  int64_t nbytes = file_system_->cache_mgr()->Pread(abs_fd, data, size, off);
1472  if (nbytes < 0) {
1473  if ( EIO == errno || EIO == -nbytes ) {
1474  PathString path;
1475  bool found = GetPathForInode(ino, &path);
1476  if ( found ) {
1478  "EIO (08) on %s", path.ToString().c_str() );
1479  } else {
1481  "EIO (08) on <unknown inode>");
1482  }
1485  }
1486  fuse_reply_err(req, -nbytes);
1487  return;
1488  }
1489  overall_bytes_fetched = nbytes;
1490  }
1491 
1492  // Push it to user
1493  fuse_reply_buf(req, data, overall_bytes_fetched);
1494  LogCvmfs(kLogCvmfs, kLogDebug, "pushed %d bytes to user",
1495  overall_bytes_fetched);
1496 }
1497 
1498 
1502 static void cvmfs_release(fuse_req_t req, fuse_ino_t ino,
1503  struct fuse_file_info *fi)
1504 {
1506 
1507  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1508  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_release on inode: %" PRIu64,
1509  uint64_t(ino));
1510 
1511 #ifdef __APPLE__
1512  if (fi->fh == kFileHandleIgnore) {
1513  fuse_reply_err(req, 0);
1514  return;
1515  }
1516 #endif
1517 
1518  int64_t fd = static_cast<int64_t>(fi->fh);
1519  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1522  }
1524 
1525  // do we have a chunked file?
1526  if (fd < 0) {
1527  const uint64_t chunk_handle = abs_fd;
1528  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk handle %" PRIu64,
1529  chunk_handle);
1530  uint64_t unique_inode;
1531  ChunkFd chunk_fd;
1532  FileChunkReflist chunks;
1533  uint32_t refctr;
1534  bool retval;
1535 
1536  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1537  chunk_tables->Lock();
1538  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1539  if (!retval) {
1540  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1541  unique_inode = ino;
1542  } else {
1543  chunk_tables->handle2uniqino.Erase(chunk_handle);
1544  }
1545  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1546  assert(retval);
1547  chunk_tables->handle2fd.Erase(chunk_handle);
1548 
1549  retval = chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1550  assert(retval);
1551  refctr--;
1552  if (refctr == 0) {
1553  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk list for inode %" PRIu64,
1554  uint64_t(unique_inode));
1555  FileChunkReflist to_delete;
1556  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &to_delete);
1557  assert(retval);
1558  chunk_tables->inode2references.Erase(unique_inode);
1559  chunk_tables->inode2chunks.Erase(unique_inode);
1560  delete to_delete.list;
1561  } else {
1562  chunk_tables->inode2references.Insert(unique_inode, refctr);
1563  }
1564  chunk_tables->Unlock();
1565 
1566  if (chunk_fd.fd != -1)
1567  file_system_->cache_mgr()->Close(chunk_fd.fd);
1569  } else {
1570  if (file_system_->cache_mgr()->Close(abs_fd) == 0) {
1572  }
1573  }
1574  fuse_reply_err(req, 0);
1575 }
1576 
1584 static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino) {
1585  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1586  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_statfs on inode: %" PRIu64,
1587  uint64_t(ino));
1588 
1589  TraceInode(Tracer::kEventStatFs, ino, "statfs()");
1590 
1592 
1593  // Unmanaged cache (no lock needed - statfs is never modified)
1596  {
1597  LogCvmfs(kLogCvmfs, kLogDebug, "QuotaManager does not support statfs");
1598  fuse_reply_statfs(req, (mount_point_->statfs_cache()->info()));
1599  return;
1600  }
1601 
1603 
1604  const uint64_t deadline = *mount_point_->statfs_cache()->expiry_deadline();
1605  struct statvfs *info = mount_point_->statfs_cache()->info();
1606 
1607  // cached version still valid
1608  if ( platform_monotonic_time() < deadline ) {
1610  fuse_reply_statfs(req, info);
1611  return;
1612  }
1613 
1614  uint64_t available = 0;
1615  uint64_t size = file_system_->cache_mgr()->quota_mgr()->GetSize();
1616  uint64_t capacity = file_system_->cache_mgr()->quota_mgr()->GetCapacity();
1617  // Fuse/OS X doesn't like values < 512
1618  info->f_bsize = info->f_frsize = 512;
1619 
1620  if (capacity == (uint64_t)(-1)) {
1621  // Unknown capacity, set capacity = size
1622  info->f_blocks = size / info->f_bsize;
1623  } else {
1624  // Take values from LRU module
1625  info->f_blocks = capacity / info->f_bsize;
1626  available = capacity - size;
1627  }
1628 
1629  info->f_bfree = info->f_bavail = available / info->f_bsize;
1630 
1631  // Inodes / entries
1632  fuse_remounter_->fence()->Enter();
1633  uint64_t all_inodes = mount_point_->catalog_mgr()->all_inodes();
1634  uint64_t loaded_inode = mount_point_->catalog_mgr()->loaded_inodes();
1635  info->f_files = all_inodes;
1636  info->f_ffree = info->f_favail = all_inodes - loaded_inode;
1637  fuse_remounter_->fence()->Leave();
1638 
1642 
1643  fuse_reply_statfs(req, info);
1644 }
1645 
1646 #ifdef __APPLE__
1647 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1648  size_t size, uint32_t position)
1649 #else
1650 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1651  size_t size)
1652 #endif
1653 {
1654  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1655  FuseInterruptCue ic(&req);
1656  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1657 
1658  fuse_remounter_->fence()->Enter();
1660  ino = catalog_mgr->MangleInode(ino);
1662  "cvmfs_getxattr on inode: %" PRIu64 " for xattr: %s",
1663  uint64_t(ino), name);
1664  if (!CheckVoms(*fuse_ctx)) {
1665  fuse_remounter_->fence()->Leave();
1666  fuse_reply_err(req, EACCES);
1667  return;
1668  }
1669  TraceInode(Tracer::kEventGetXAttr, ino, "getxattr()");
1670 
1671  const string attr = name;
1673  const bool found = GetDirentForInode(ino, &d);
1674  bool retval;
1675  XattrList xattrs;
1676 
1677  PathString path;
1678  retval = GetPathForInode(ino, &path);
1679  assert(retval);
1680  if (d.IsLink()) {
1681  catalog::LookupOptions lookup_options = static_cast<catalog::LookupOptions>(
1683  catalog::DirectoryEntry raw_symlink;
1684  retval = catalog_mgr->LookupPath(path, lookup_options, &raw_symlink);
1685  assert(retval);
1686  d.set_symlink(raw_symlink.symlink());
1687  }
1688  if (d.HasXattrs()) {
1689  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1690 
1692  "cvmfs_statfs: Race condition? "
1693  "LookupXattrs did not succeed for path %s",
1694  path.c_str())) {
1695  fuse_remounter_->fence()->Leave();
1696  fuse_reply_err(req, ESTALE);
1697  }
1698  }
1699 
1700  bool magic_xattr_success = true;
1702  attr, path, &d));
1703  if (!magic_xattr.IsNull()) {
1704  magic_xattr_success = magic_xattr->
1705  PrepareValueFencedProtected(fuse_ctx->gid);
1706  }
1707 
1708  fuse_remounter_->fence()->Leave();
1709 
1710  if (!found) {
1711  ReplyNegative(d, req);
1712  return;
1713  }
1714 
1715  if (!magic_xattr_success) {
1716  fuse_reply_err(req, ENOATTR);
1717  return;
1718  }
1719 
1720  string attribute_value;
1721 
1722  if (!magic_xattr.IsNull()) {
1723  attribute_value = magic_xattr->GetValue();
1724  } else {
1725  if (!xattrs.Get(attr, &attribute_value)) {
1726  fuse_reply_err(req, ENOATTR);
1727  return;
1728  }
1729  }
1730 
1731  if (size == 0) {
1732  fuse_reply_xattr(req, attribute_value.length());
1733  } else if (size >= attribute_value.length()) {
1734  fuse_reply_buf(req, &attribute_value[0], attribute_value.length());
1735  } else {
1736  fuse_reply_err(req, ERANGE);
1737  }
1738 }
1739 
1740 
1741 static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
1742  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1743  FuseInterruptCue ic(&req);
1744  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1745 
1746  fuse_remounter_->fence()->Enter();
1748  ino = catalog_mgr->MangleInode(ino);
1749  TraceInode(Tracer::kEventListAttr, ino, "listxattr()");
1751  "cvmfs_listxattr on inode: %" PRIu64 ", size %u [visibility %d]",
1752  uint64_t(ino), size,
1754 
1756  const bool found = GetDirentForInode(ino, &d);
1757  XattrList xattrs;
1758  if (d.HasXattrs()) {
1759  PathString path;
1760  bool retval = GetPathForInode(ino, &path);
1761 
1763  "cvmfs_listxattr: Race condition? "
1764  "GetPathForInode did not succeed for ino %lu",
1765  ino)) {
1766  fuse_remounter_->fence()->Leave();
1767  fuse_reply_err(req, ESTALE);
1768  return;
1769  }
1770 
1771  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1773  "cvmfs_listxattr: Race condition? "
1774  "LookupXattrs did not succeed for ino %lu",
1775  ino)) {
1776  fuse_remounter_->fence()->Leave();
1777  fuse_reply_err(req, ESTALE);
1778  return;
1779  }
1780  }
1781  fuse_remounter_->fence()->Leave();
1782 
1783  if (!found) {
1784  ReplyNegative(d, req);
1785  return;
1786  }
1787 
1788  string attribute_list;
1789  attribute_list = mount_point_->magic_xattr_mgr()->GetListString(&d);
1790  attribute_list += xattrs.ListKeysPosix(attribute_list);
1791 
1792  if (size == 0) {
1793  fuse_reply_xattr(req, attribute_list.length());
1794  } else if (size >= attribute_list.length()) {
1795  if (attribute_list.empty())
1796  fuse_reply_buf(req, NULL, 0);
1797  else
1798  fuse_reply_buf(req, &attribute_list[0], attribute_list.length());
1799  } else {
1800  fuse_reply_err(req, ERANGE);
1801  }
1802 }
1803 
1804 bool Evict(const string &path) {
1805  catalog::DirectoryEntry dirent;
1806  fuse_remounter_->fence()->Enter();
1807  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1808 
1809  if (!found || !dirent.IsRegular()) {
1810  fuse_remounter_->fence()->Leave();
1811  return false;
1812  }
1813 
1814  if (!dirent.IsChunkedFile()) {
1815  fuse_remounter_->fence()->Leave();
1816  } else {
1817  FileChunkList chunks;
1819  PathString(path), dirent.hash_algorithm(), &chunks);
1820  fuse_remounter_->fence()->Leave();
1821  for (unsigned i = 0; i < chunks.size(); ++i) {
1823  ->Remove(chunks.AtPtr(i)->content_hash());
1824  }
1825  }
1826  file_system_->cache_mgr()->quota_mgr()->Remove(dirent.checksum());
1827  return true;
1828 }
1829 
1830 
1831 bool Pin(const string &path) {
1832  catalog::DirectoryEntry dirent;
1833  fuse_remounter_->fence()->Enter();
1834  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1835  if (!found || !dirent.IsRegular()) {
1836  fuse_remounter_->fence()->Leave();
1837  return false;
1838  }
1839 
1840  if (!dirent.IsChunkedFile()) {
1841  fuse_remounter_->fence()->Leave();
1842  } else {
1843  FileChunkList chunks;
1845  PathString(path), dirent.hash_algorithm(), &chunks);
1846  fuse_remounter_->fence()->Leave();
1847  for (unsigned i = 0; i < chunks.size(); ++i) {
1848  bool retval =
1850  chunks.AtPtr(i)->content_hash(),
1851  chunks.AtPtr(i)->size(),
1852  "Part of " + path,
1853  false);
1854  if (!retval)
1855  return false;
1856  int fd = -1;
1857  if (dirent.IsExternalFile()) {
1859  chunks.AtPtr(i)->content_hash(),
1860  chunks.AtPtr(i)->size(),
1861  "Part of " + path,
1862  dirent.compression_algorithm(),
1864  path,
1865  chunks.AtPtr(i)->offset());
1866  } else {
1867  fd = mount_point_->fetcher()->Fetch(
1868  chunks.AtPtr(i)->content_hash(),
1869  chunks.AtPtr(i)->size(),
1870  "Part of " + path,
1871  dirent.compression_algorithm(),
1873  }
1874  if (fd < 0) {
1875  return false;
1876  }
1877  file_system_->cache_mgr()->Close(fd);
1878  }
1879  return true;
1880  }
1881 
1882  bool retval = file_system_->cache_mgr()->quota_mgr()->Pin(
1883  dirent.checksum(), dirent.size(), path, false);
1884  if (!retval)
1885  return false;
1886  Fetcher *this_fetcher = dirent.IsExternalFile()
1888  : mount_point_->fetcher();
1889  int fd = this_fetcher->Fetch(
1890  dirent.checksum(), dirent.size(), path, dirent.compression_algorithm(),
1892  if (fd < 0) {
1893  return false;
1894  }
1895  file_system_->cache_mgr()->Close(fd);
1896  return true;
1897 }
1898 
1899 
1903 static void cvmfs_init(void *userdata, struct fuse_conn_info *conn) {
1904  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_init");
1905 
1906  // NFS support
1907 #ifdef CVMFS_NFS_SUPPORT
1908  conn->want |= FUSE_CAP_EXPORT_SUPPORT;
1909 #endif
1910 
1911  if (mount_point_->enforce_acls()) {
1912 #ifdef FUSE_CAP_POSIX_ACL
1913  if ((conn->capable & FUSE_CAP_POSIX_ACL) == 0) {
1915  "ACL support requested but missing fuse kernel support, "
1916  "aborting");
1917  }
1918  conn->want |= FUSE_CAP_POSIX_ACL;
1919  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, "enforcing ACLs");
1920 #else
1922  "ACL support requested but not available in this version of "
1923  "libfuse, aborting");
1924 #endif
1925  }
1926 
1927  if ( mount_point_->cache_symlinks() ) {
1928 #ifdef FUSE_CAP_CACHE_SYMLINKS
1929  if ((conn->capable & FUSE_CAP_CACHE_SYMLINKS) == FUSE_CAP_CACHE_SYMLINKS) {
1930  conn->want |= FUSE_CAP_CACHE_SYMLINKS;
1931  LogCvmfs(kLogCvmfs, kLogDebug, "FUSE: "
1932  "Enable symlink caching");
1933  #ifndef FUSE_CAP_EXPIRE_ONLY
1935  "FUSE: Symlink caching enabled but no support for fuse_expire_entry, "
1936  "mountpoints on top of symlinks will break!");
1937  #endif
1938  } else {
1941  "FUSE: Symlink caching requested but missing fuse kernel support, "
1942  "falling back to no caching");
1943  }
1944 #else
1947  "FUSE: Symlink caching requested but missing libfuse support, "
1948  "falling back to no caching");
1949 #endif
1950  }
1951 
1952 #ifdef FUSE_CAP_EXPIRE_ONLY
1953  if ((conn->capable & FUSE_CAP_EXPIRE_ONLY) == FUSE_CAP_EXPIRE_ONLY) {
1955  LogCvmfs(kLogCvmfs, kLogDebug, "FUSE: "
1956  "Enable fuse_expire_entry");
1957  } else if (mount_point_->cache_symlinks()) {
1959  "FUSE: Symlink caching enabled but no support for fuse_expire_entry, "
1960  "mountpoints on top of symlinks will break!");
1961  }
1962 #endif
1963 }
1964 
1965 static void cvmfs_destroy(void *unused __attribute__((unused))) {
1966  // The debug log is already closed at this point
1967  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_destroy");
1968 }
1969 
1973 static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations) {
1974  memset(cvmfs_operations, 0, sizeof(*cvmfs_operations));
1975 
1976  // Init/Fini
1977  cvmfs_operations->init = cvmfs_init;
1978  cvmfs_operations->destroy = cvmfs_destroy;
1979 
1980  cvmfs_operations->lookup = cvmfs_lookup;
1981  cvmfs_operations->getattr = cvmfs_getattr;
1982  cvmfs_operations->readlink = cvmfs_readlink;
1983  cvmfs_operations->open = cvmfs_open;
1984  cvmfs_operations->read = cvmfs_read;
1985  cvmfs_operations->release = cvmfs_release;
1986  cvmfs_operations->opendir = cvmfs_opendir;
1987  cvmfs_operations->readdir = cvmfs_readdir;
1988  cvmfs_operations->releasedir = cvmfs_releasedir;
1989  cvmfs_operations->statfs = cvmfs_statfs;
1990  cvmfs_operations->getxattr = cvmfs_getxattr;
1991  cvmfs_operations->listxattr = cvmfs_listxattr;
1992  cvmfs_operations->forget = cvmfs_forget;
1993 #if (FUSE_VERSION >= 29)
1994  cvmfs_operations->forget_multi = cvmfs_forget_multi;
1995 #endif
1996 }
1997 
1998 // Called by cvmfs_talk when switching into read-only cache mode
2000  if (cvmfs::unpin_listener_) {
2002  cvmfs::unpin_listener_ = NULL;
2003  }
2007  }
2008 }
2009 
2010 bool SendFuseFd(const std::string &socket_path) {
2011  int fuse_fd;
2012 #if (FUSE_VERSION >= 30)
2013  fuse_fd = fuse_session_fd(*reinterpret_cast<struct fuse_session**>(
2015 #else
2016  fuse_fd = fuse_chan_fd(*reinterpret_cast<struct fuse_chan**>(
2018 #endif
2019  assert(fuse_fd >= 0);
2020  int sock_fd = ConnectSocket(socket_path);
2021  if (sock_fd < 0) {
2022  LogCvmfs(kLogCvmfs, kLogDebug, "cannot connect to socket %s: %d",
2023  socket_path.c_str(), errno);
2024  return false;
2025  }
2026  bool retval = SendFd2Socket(sock_fd, fuse_fd);
2027  close(sock_fd);
2028  return retval;
2029 }
2030 
2031 } // namespace cvmfs
2032 
2033 
2034 string *g_boot_error = NULL;
2035 
2036 __attribute__((visibility("default")))
2037 loader::CvmfsExports *g_cvmfs_exports = NULL;
2038 
2045 
2046  virtual bool PrepareValueFenced() {
2047  catalogs_valid_until_ = cvmfs::fuse_remounter_->catalogs_valid_until();
2048  return true;
2049  }
2050 
2051  virtual std::string GetValue() {
2052  if (catalogs_valid_until_ == MountPoint::kIndefiniteDeadline) {
2053  return "never (fixed root catalog)";
2054  } else {
2055  time_t now = time(NULL);
2056  return StringifyInt( (catalogs_valid_until_ - now) / 60);
2057  }
2058  }
2059 };
2060 
2062  virtual std::string GetValue() {
2063  return StringifyInt(
2064  cvmfs::inode_generation_info_.inode_generation +
2065  xattr_mgr_->mount_point()->catalog_mgr()->inode_gauge());
2066  }
2067 };
2068 
2070  virtual std::string GetValue() {
2072  }
2073 };
2074 
2076  virtual std::string GetValue() { return StringifyInt(cvmfs::pid_); }
2077 };
2078 
2080  virtual std::string GetValue() {
2081  time_t now = time(NULL);
2082  uint64_t uptime = now - cvmfs::loader_exports_->boot_time;
2083  return StringifyInt(uptime / 60);
2084  }
2085 };
2086 
2091 static void RegisterMagicXattrs() {
2093  mgr->Register("user.expires", new ExpiresMagicXattr());
2094  mgr->Register("user.inode_max", new InodeMaxMagicXattr());
2095  mgr->Register("user.pid", new PidMagicXattr());
2096  mgr->Register("user.maxfd", new MaxFdMagicXattr());
2097  mgr->Register("user.uptime", new UptimeMagicXattr());
2098 
2099  mgr->Freeze();
2100 }
2101 
2107  const string &mount_path,
2108  const string &fqrn,
2110 {
2111  fs_info.wait_workspace = false;
2112  FileSystem *file_system = FileSystem::Create(fs_info);
2113 
2114  if (file_system->boot_status() == loader::kFailLockWorkspace) {
2115  string fqrn_from_xattr;
2116  int retval = platform_getxattr(mount_path, "user.fqrn", &fqrn_from_xattr);
2117  if (!retval) {
2118  // Cvmfs not mounted anymore, but another cvmfs process is still in
2119  // shutdown procedure. Try again and wait for lock
2120  delete file_system;
2121  fs_info.wait_workspace = true;
2122  file_system = FileSystem::Create(fs_info);
2123  } else {
2124  if (fqrn_from_xattr == fqrn) {
2126  "repository already mounted on %s", mount_path.c_str());
2128  } else {
2130  "CernVM-FS repository %s already mounted on %s",
2131  fqrn.c_str(), mount_path.c_str());
2133  }
2134  }
2135  }
2136 
2137  return file_system;
2138 }
2139 
2140 
2141 static void InitOptionsMgr(const loader::LoaderExports *loader_exports) {
2142  if (loader_exports->version >= 3 && loader_exports->simple_options_parsing) {
2144  new DefaultOptionsTemplateManager(loader_exports->repository_name));
2145  } else {
2147  new DefaultOptionsTemplateManager(loader_exports->repository_name));
2148  }
2149 
2150  if (loader_exports->config_files != "") {
2151  vector<string> tokens = SplitString(loader_exports->config_files, ':');
2152  for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
2153  cvmfs::options_mgr_->ParsePath(tokens[i], false);
2154  }
2155  } else {
2157  }
2158 }
2159 
2160 
2161 static unsigned CheckMaxOpenFiles() {
2162  static unsigned max_open_files;
2163  static bool already_done = false;
2164 
2165  // check number of open files (lazy evaluation)
2166  if (!already_done) {
2167  unsigned soft_limit = 0;
2168  unsigned hard_limit = 0;
2169  GetLimitNoFile(&soft_limit, &hard_limit);
2170 
2171  if (soft_limit < cvmfs::kMinOpenFiles) {
2173  "Warning: current limits for number of open files are "
2174  "(%lu/%lu)\n"
2175  "CernVM-FS is likely to run out of file descriptors, "
2176  "set ulimit -n to at least %lu",
2177  soft_limit, hard_limit, cvmfs::kMinOpenFiles);
2178  }
2179  max_open_files = soft_limit;
2180  already_done = true;
2181  }
2182 
2183  return max_open_files;
2184 }
2185 
2186 
2187 static int Init(const loader::LoaderExports *loader_exports) {
2188  g_boot_error = new string("unknown error");
2189  cvmfs::loader_exports_ = loader_exports;
2190 
2192 
2193  InitOptionsMgr(loader_exports);
2194 
2195  // We need logging set up before forking the watchdog
2197  *cvmfs::options_mgr_, loader_exports->repository_name);
2198 
2199  // Monitor, check for maximum number of open files
2200  if (cvmfs::UseWatchdog()) {
2201  auto_umount::SetMountpoint(loader_exports->mount_point);
2203  if (cvmfs::watchdog_ == NULL) {
2204  *g_boot_error = "failed to initialize watchdog.";
2205  return loader::kFailMonitor;
2206  }
2207  }
2209 
2211  fs_info.type = FileSystem::kFsFuse;
2212  fs_info.name = loader_exports->repository_name;
2213  fs_info.exe_path = loader_exports->program_name;
2214  fs_info.options_mgr = cvmfs::options_mgr_;
2215  fs_info.foreground = loader_exports->foreground;
2217  loader_exports->mount_point,
2218  loader_exports->repository_name,
2219  fs_info);
2220  if (!cvmfs::file_system_->IsValid()) {
2222  return cvmfs::file_system_->boot_status();
2223  }
2224 
2227  if (!cvmfs::mount_point_->IsValid()) {
2229  return cvmfs::mount_point_->boot_status();
2230  }
2231 
2233 
2235  cvmfs::directory_handles_->set_empty_key((uint64_t)(-1));
2236  cvmfs::directory_handles_->set_deleted_key((uint64_t)(-2));
2237 
2238  LogCvmfs(kLogCvmfs, kLogDebug, "fuse inode size is %d bits",
2239  sizeof(fuse_ino_t) * 8);
2240 
2245  LogCvmfs(kLogCvmfs, kLogDebug, "root inode is %" PRIu64,
2246  uint64_t(cvmfs::mount_point_->catalog_mgr()->GetRootInode()));
2247 
2248  void **channel_or_session = NULL;
2249  if (loader_exports->version >= 4) {
2250  channel_or_session = loader_exports->fuse_channel_or_session;
2251  }
2252 
2253  bool fuse_notify_invalidation = true;
2254  std::string buf;
2255  if (cvmfs::options_mgr_->GetValue("CVMFS_FUSE_NOTIFY_INVALIDATION",
2256  &buf)) {
2257  if (!cvmfs::options_mgr_->IsOn(buf)) {
2258  fuse_notify_invalidation = false;
2260  }
2261  }
2264  channel_or_session, fuse_notify_invalidation);
2265 
2266  // Control & command interface
2268  cvmfs::mount_point_->talk_socket_path(),
2271  if ((cvmfs::mount_point_->talk_socket_uid() != 0) ||
2272  (cvmfs::mount_point_->talk_socket_gid() != 0))
2273  {
2274  uid_t tgt_uid = cvmfs::mount_point_->talk_socket_uid();
2275  gid_t tgt_gid = cvmfs::mount_point_->talk_socket_gid();
2276  int rvi = chown(cvmfs::mount_point_->talk_socket_path().c_str(),
2277  tgt_uid, tgt_gid);
2278  if (rvi != 0) {
2279  *g_boot_error = std::string("failed to set talk socket ownership - ")
2280  + "target " + StringifyInt(tgt_uid) + ":" + StringifyInt(tgt_uid)
2281  + ", user " + StringifyInt(geteuid()) + ":" + StringifyInt(getegid());
2282  return loader::kFailTalk;
2283  }
2284  }
2285  if (cvmfs::talk_mgr_ == NULL) {
2286  *g_boot_error = "failed to initialize talk socket (" +
2287  StringifyInt(errno) + ")";
2288  return loader::kFailTalk;
2289  }
2290 
2291  // Notification system client
2292  {
2294  if (options->IsDefined("CVMFS_NOTIFICATION_SERVER")) {
2295  std::string config;
2296  options->GetValue("CVMFS_NOTIFICATION_SERVER", &config);
2297  const std::string repo_name = cvmfs::mount_point_->fqrn();
2299  new NotificationClient(config, repo_name, cvmfs::fuse_remounter_,
2300  cvmfs::mount_point_->download_mgr(),
2302  }
2303  }
2304 
2305  return loader::kFailOk;
2306 }
2307 
2308 
2312 static void Spawn() {
2313  // First thing: kick off the watchdog while we still have a single-threaded
2314  // well-defined state
2315  cvmfs::pid_ = getpid();
2316  if (cvmfs::watchdog_) {
2317  cvmfs::watchdog_->Spawn(GetCurrentWorkingDirectory() + "/stacktrace." +
2318  cvmfs::mount_point_->fqrn());
2319  }
2320 
2322  if (cvmfs::mount_point_->dentry_tracker()->is_active()) {
2324  // Usually every minute
2325  static_cast<unsigned int>(cvmfs::mount_point_->kcache_timeout_sec()));
2326  }
2327 
2330  if (cvmfs::mount_point_->resolv_conf_watcher() != NULL) {
2332  }
2334  quota_mgr->Spawn();
2335  if (quota_mgr->HasCapability(QuotaManager::kCapListeners)) {
2337  quota_mgr,
2338  cvmfs::mount_point_->uuid()->uuid() + "-watchdog");
2340  quota_mgr,
2341  cvmfs::mount_point_->catalog_mgr(),
2342  cvmfs::mount_point_->uuid()->uuid() + "-unpin");
2343  }
2346 
2347  if (cvmfs::notification_client_ != NULL) {
2349  }
2350 
2351  if (cvmfs::file_system_->nfs_maps() != NULL) {
2353  }
2354 
2356 
2357  if (cvmfs::mount_point_->telemetry_aggr() != NULL) {
2359  }
2360 }
2361 
2362 
2363 static string GetErrorMsg() {
2364  if (g_boot_error)
2365  return *g_boot_error;
2366  return "";
2367 }
2368 
2369 
2375 static void ShutdownMountpoint() {
2376  delete cvmfs::talk_mgr_;
2377  cvmfs::talk_mgr_ = NULL;
2378 
2381 
2382  // The remonter has a reference to the mount point and the inode generation
2383  delete cvmfs::fuse_remounter_;
2384  cvmfs::fuse_remounter_ = NULL;
2385 
2386  // The unpin listener requires the catalog, so this must be unregistered
2387  // before the catalog manager is removed
2388  if (cvmfs::unpin_listener_ != NULL) {
2390  cvmfs::unpin_listener_ = NULL;
2391  }
2392  if (cvmfs::watchdog_listener_ != NULL) {
2395  }
2396 
2398  delete cvmfs::mount_point_;
2400  cvmfs::mount_point_ = NULL;
2401 }
2402 
2403 
2404 static void Fini() {
2406 
2407  delete cvmfs::file_system_;
2408  delete cvmfs::options_mgr_;
2409  cvmfs::file_system_ = NULL;
2410  cvmfs::options_mgr_ = NULL;
2411 
2412  delete cvmfs::watchdog_;
2413  cvmfs::watchdog_ = NULL;
2414 
2415  delete g_boot_error;
2416  g_boot_error = NULL;
2418 
2420 }
2421 
2422 
2423 static int AltProcessFlavor(int argc, char **argv) {
2424  if (strcmp(argv[1], "__cachemgr__") == 0) {
2425  return PosixQuotaManager::MainCacheManager(argc, argv);
2426  }
2427  if (strcmp(argv[1], "__wpad__") == 0) {
2428  return download::MainResolveProxyDescription(argc, argv);
2429  }
2430  return 1;
2431 }
2432 
2433 
2434 static bool MaintenanceMode(const int fd_progress) {
2435  SendMsg2Socket(fd_progress, "Entering maintenance mode\n");
2436  string msg_progress = "Draining out kernel caches (";
2438  msg_progress += "up to ";
2439  msg_progress += StringifyInt(static_cast<int>(
2440  cvmfs::mount_point_->kcache_timeout_sec())) +
2441  "s)\n";
2442  SendMsg2Socket(fd_progress, msg_progress);
2444  return true;
2445 }
2446 
2447 
2448 static bool SaveState(const int fd_progress, loader::StateList *saved_states) {
2449  string msg_progress;
2450 
2451  unsigned num_open_dirs = cvmfs::directory_handles_->size();
2452  if (num_open_dirs != 0) {
2453 #ifdef DEBUGMSG
2454  for (cvmfs::DirectoryHandles::iterator i =
2455  cvmfs::directory_handles_->begin(),
2456  iEnd = cvmfs::directory_handles_->end(); i != iEnd; ++i)
2457  {
2458  LogCvmfs(kLogCvmfs, kLogDebug, "saving dirhandle %d", i->first);
2459  }
2460 #endif
2461 
2462  msg_progress = "Saving open directory handles (" +
2463  StringifyInt(num_open_dirs) + " handles)\n";
2464  SendMsg2Socket(fd_progress, msg_progress);
2465 
2466  // TODO(jblomer): should rather be saved just in a malloc'd memory block
2467  cvmfs::DirectoryHandles *saved_handles =
2469  loader::SavedState *save_open_dirs = new loader::SavedState();
2470  save_open_dirs->state_id = loader::kStateOpenDirs;
2471  save_open_dirs->state = saved_handles;
2472  saved_states->push_back(save_open_dirs);
2473  }
2474 
2475  if (!cvmfs::file_system_->IsNfsSource()) {
2476  msg_progress = "Saving inode tracker\n";
2477  SendMsg2Socket(fd_progress, msg_progress);
2478  glue::InodeTracker *saved_inode_tracker =
2479  new glue::InodeTracker(*cvmfs::mount_point_->inode_tracker());
2480  loader::SavedState *state_glue_buffer = new loader::SavedState();
2481  state_glue_buffer->state_id = loader::kStateGlueBufferV4;
2482  state_glue_buffer->state = saved_inode_tracker;
2483  saved_states->push_back(state_glue_buffer);
2484  }
2485 
2486  msg_progress = "Saving negative entry cache\n";
2487  SendMsg2Socket(fd_progress, msg_progress);
2488  glue::DentryTracker *saved_dentry_tracker =
2489  new glue::DentryTracker(*cvmfs::mount_point_->dentry_tracker());
2490  loader::SavedState *state_dentry_tracker = new loader::SavedState();
2491  state_dentry_tracker->state_id = loader::kStateDentryTracker;
2492  state_dentry_tracker->state = saved_dentry_tracker;
2493  saved_states->push_back(state_dentry_tracker);
2494 
2495  msg_progress = "Saving page cache entry tracker\n";
2496  SendMsg2Socket(fd_progress, msg_progress);
2497  glue::PageCacheTracker *saved_page_cache_tracker =
2498  new glue::PageCacheTracker(*cvmfs::mount_point_->page_cache_tracker());
2499  loader::SavedState *state_page_cache_tracker = new loader::SavedState();
2500  state_page_cache_tracker->state_id = loader::kStatePageCacheTracker;
2501  state_page_cache_tracker->state = saved_page_cache_tracker;
2502  saved_states->push_back(state_page_cache_tracker);
2503 
2504  msg_progress = "Saving chunk tables\n";
2505  SendMsg2Socket(fd_progress, msg_progress);
2506  ChunkTables *saved_chunk_tables = new ChunkTables(
2507  *cvmfs::mount_point_->chunk_tables());
2508  loader::SavedState *state_chunk_tables = new loader::SavedState();
2509  state_chunk_tables->state_id = loader::kStateOpenChunksV4;
2510  state_chunk_tables->state = saved_chunk_tables;
2511  saved_states->push_back(state_chunk_tables);
2512 
2513  msg_progress = "Saving inode generation\n";
2514  SendMsg2Socket(fd_progress, msg_progress);
2517  cvmfs::InodeGenerationInfo *saved_inode_generation =
2519  loader::SavedState *state_inode_generation = new loader::SavedState();
2520  state_inode_generation->state_id = loader::kStateInodeGeneration;
2521  state_inode_generation->state = saved_inode_generation;
2522  saved_states->push_back(state_inode_generation);
2523 
2524  // Close open file catalogs
2526 
2527  loader::SavedState *state_cache_mgr = new loader::SavedState();
2528  state_cache_mgr->state_id = loader::kStateOpenFiles;
2529  state_cache_mgr->state =
2530  cvmfs::file_system_->cache_mgr()->SaveState(fd_progress);
2531  saved_states->push_back(state_cache_mgr);
2532 
2533  msg_progress = "Saving open files counter\n";
2534  uint32_t *saved_num_fd =
2535  new uint32_t(cvmfs::file_system_->no_open_files()->Get());
2536  loader::SavedState *state_num_fd = new loader::SavedState();
2537  state_num_fd->state_id = loader::kStateOpenFilesCounter;
2538  state_num_fd->state = saved_num_fd;
2539  saved_states->push_back(state_num_fd);
2540 
2541  return true;
2542 }
2543 
2544 
2545 static bool RestoreState(const int fd_progress,
2546  const loader::StateList &saved_states)
2547 {
2548  // If we have no saved version of the page cache tracker, it is unsafe
2549  // to start using it. The page cache tracker has to run for the entire
2550  // lifetime of the mountpoint or not at all.
2552 
2553  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2554  if (saved_states[i]->state_id == loader::kStateOpenDirs) {
2555  SendMsg2Socket(fd_progress, "Restoring open directory handles... ");
2557  cvmfs::DirectoryHandles *saved_handles =
2558  (cvmfs::DirectoryHandles *)saved_states[i]->state;
2559  cvmfs::directory_handles_ = new cvmfs::DirectoryHandles(*saved_handles);
2562  cvmfs::DirectoryHandles::const_iterator i =
2563  cvmfs::directory_handles_->begin();
2564  for (; i != cvmfs::directory_handles_->end(); ++i) {
2565  if (i->first >= cvmfs::next_directory_handle_)
2566  cvmfs::next_directory_handle_ = i->first + 1;
2567  }
2568 
2569  SendMsg2Socket(fd_progress,
2570  StringifyInt(cvmfs::directory_handles_->size()) + " handles\n");
2571  }
2572 
2573  if (saved_states[i]->state_id == loader::kStateGlueBuffer) {
2574  SendMsg2Socket(fd_progress, "Migrating inode tracker (v1 to v4)... ");
2575  compat::inode_tracker::InodeTracker *saved_inode_tracker =
2576  (compat::inode_tracker::InodeTracker *)saved_states[i]->state;
2578  saved_inode_tracker, cvmfs::mount_point_->inode_tracker());
2579  SendMsg2Socket(fd_progress, " done\n");
2580  }
2581 
2582  if (saved_states[i]->state_id == loader::kStateGlueBufferV2) {
2583  SendMsg2Socket(fd_progress, "Migrating inode tracker (v2 to v4)... ");
2584  compat::inode_tracker_v2::InodeTracker *saved_inode_tracker =
2585  (compat::inode_tracker_v2::InodeTracker *)saved_states[i]->state;
2586  compat::inode_tracker_v2::Migrate(saved_inode_tracker,
2587  cvmfs::mount_point_->inode_tracker());
2588  SendMsg2Socket(fd_progress, " done\n");
2589  }
2590 
2591  if (saved_states[i]->state_id == loader::kStateGlueBufferV3) {
2592  SendMsg2Socket(fd_progress, "Migrating inode tracker (v3 to v4)... ");
2593  compat::inode_tracker_v3::InodeTracker *saved_inode_tracker =
2594  (compat::inode_tracker_v3::InodeTracker *)saved_states[i]->state;
2595  compat::inode_tracker_v3::Migrate(saved_inode_tracker,
2596  cvmfs::mount_point_->inode_tracker());
2597  SendMsg2Socket(fd_progress, " done\n");
2598  }
2599 
2600  if (saved_states[i]->state_id == loader::kStateGlueBufferV4) {
2601  SendMsg2Socket(fd_progress, "Restoring inode tracker... ");
2603  glue::InodeTracker *saved_inode_tracker =
2604  (glue::InodeTracker *)saved_states[i]->state;
2606  glue::InodeTracker(*saved_inode_tracker);
2607  SendMsg2Socket(fd_progress, " done\n");
2608  }
2609 
2610  if (saved_states[i]->state_id == loader::kStateDentryTracker) {
2611  SendMsg2Socket(fd_progress, "Restoring dentry tracker... ");
2613  glue::DentryTracker *saved_dentry_tracker =
2614  static_cast<glue::DentryTracker *>(saved_states[i]->state);
2616  glue::DentryTracker(*saved_dentry_tracker);
2617  SendMsg2Socket(fd_progress, " done\n");
2618  }
2619 
2620  if (saved_states[i]->state_id == loader::kStatePageCacheTracker) {
2621  SendMsg2Socket(fd_progress, "Restoring page cache entry tracker... ");
2623  glue::PageCacheTracker *saved_page_cache_tracker =
2624  (glue::PageCacheTracker *)saved_states[i]->state;
2626  glue::PageCacheTracker(*saved_page_cache_tracker);
2627  SendMsg2Socket(fd_progress, " done\n");
2628  }
2629 
2630  ChunkTables *chunk_tables = cvmfs::mount_point_->chunk_tables();
2631 
2632  if (saved_states[i]->state_id == loader::kStateOpenChunks) {
2633  SendMsg2Socket(fd_progress, "Migrating chunk tables (v1 to v4)... ");
2634  compat::chunk_tables::ChunkTables *saved_chunk_tables =
2635  (compat::chunk_tables::ChunkTables *)saved_states[i]->state;
2636  compat::chunk_tables::Migrate(saved_chunk_tables, chunk_tables);
2637  SendMsg2Socket(fd_progress,
2638  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2639  }
2640 
2641  if (saved_states[i]->state_id == loader::kStateOpenChunksV2) {
2642  SendMsg2Socket(fd_progress, "Migrating chunk tables (v2 to v4)... ");
2643  compat::chunk_tables_v2::ChunkTables *saved_chunk_tables =
2644  (compat::chunk_tables_v2::ChunkTables *)saved_states[i]->state;
2645  compat::chunk_tables_v2::Migrate(saved_chunk_tables, chunk_tables);
2646  SendMsg2Socket(fd_progress,
2647  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2648  }
2649 
2650  if (saved_states[i]->state_id == loader::kStateOpenChunksV3) {
2651  SendMsg2Socket(fd_progress, "Migrating chunk tables (v3 to v4)... ");
2652  compat::chunk_tables_v3::ChunkTables *saved_chunk_tables =
2653  (compat::chunk_tables_v3::ChunkTables *)saved_states[i]->state;
2654  compat::chunk_tables_v3::Migrate(saved_chunk_tables, chunk_tables);
2655  SendMsg2Socket(fd_progress,
2656  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2657  }
2658 
2659  if (saved_states[i]->state_id == loader::kStateOpenChunksV4) {
2660  SendMsg2Socket(fd_progress, "Restoring chunk tables... ");
2661  chunk_tables->~ChunkTables();
2662  ChunkTables *saved_chunk_tables = reinterpret_cast<ChunkTables *>(
2663  saved_states[i]->state);
2664  new (chunk_tables) ChunkTables(*saved_chunk_tables);
2665  SendMsg2Socket(fd_progress, " done\n");
2666  }
2667 
2668  if (saved_states[i]->state_id == loader::kStateInodeGeneration) {
2669  SendMsg2Socket(fd_progress, "Restoring inode generation... ");
2670  cvmfs::InodeGenerationInfo *old_info =
2671  (cvmfs::InodeGenerationInfo *)saved_states[i]->state;
2672  if (old_info->version == 1) {
2673  // Migration
2675  old_info->initial_revision;
2677  // Note: in the rare case of inode generation being 0 before, inode
2678  // can clash after reload before remount
2679  } else {
2680  cvmfs::inode_generation_info_ = *old_info;
2681  }
2683  SendMsg2Socket(fd_progress, " done\n");
2684  }
2685 
2686  if (saved_states[i]->state_id == loader::kStateOpenFilesCounter) {
2687  SendMsg2Socket(fd_progress, "Restoring open files counter... ");
2688  cvmfs::file_system_->no_open_files()->Set(*(reinterpret_cast<uint32_t *>(
2689  saved_states[i]->state)));
2690  SendMsg2Socket(fd_progress, " done\n");
2691  }
2692 
2693  if (saved_states[i]->state_id == loader::kStateOpenFiles) {
2694  int new_root_fd = cvmfs::file_system_->cache_mgr()->RestoreState(
2695  fd_progress, saved_states[i]->state);
2696  LogCvmfs(kLogCvmfs, kLogDebug, "new root file catalog descriptor @%d",
2697  new_root_fd);
2698  if (new_root_fd >= 0) {
2699  cvmfs::file_system_->RemapCatalogFd(0, new_root_fd);
2700  }
2701  }
2702  }
2703  if (cvmfs::mount_point_->inode_annotation()) {
2704  uint64_t saved_generation = cvmfs::inode_generation_info_.inode_generation;
2705  cvmfs::mount_point_->inode_annotation()->IncGeneration(saved_generation);
2706  }
2707 
2708  return true;
2709 }
2710 
2711 
2712 static void FreeSavedState(const int fd_progress,
2713  const loader::StateList &saved_states)
2714 {
2715  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2716  switch (saved_states[i]->state_id) {
2718  SendMsg2Socket(fd_progress, "Releasing saved open directory handles\n");
2719  delete static_cast<cvmfs::DirectoryHandles *>(saved_states[i]->state);
2720  break;
2723  fd_progress, "Releasing saved glue buffer (version 1)\n");
2724  delete static_cast<compat::inode_tracker::InodeTracker *>(
2725  saved_states[i]->state);
2726  break;
2729  fd_progress, "Releasing saved glue buffer (version 2)\n");
2730  delete static_cast<compat::inode_tracker_v2::InodeTracker *>(
2731  saved_states[i]->state);
2732  break;
2735  fd_progress, "Releasing saved glue buffer (version 3)\n");
2736  delete static_cast<compat::inode_tracker_v3::InodeTracker *>(
2737  saved_states[i]->state);
2738  break;
2740  SendMsg2Socket(fd_progress, "Releasing saved glue buffer\n");
2741  delete static_cast<glue::InodeTracker *>(saved_states[i]->state);
2742  break;
2744  SendMsg2Socket(fd_progress, "Releasing saved dentry tracker\n");
2745  delete static_cast<glue::DentryTracker *>(saved_states[i]->state);
2746  break;
2748  SendMsg2Socket(fd_progress, "Releasing saved page cache entry cache\n");
2749  delete static_cast<glue::PageCacheTracker *>(saved_states[i]->state);
2750  break;
2752  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 1)\n");
2753  delete static_cast<compat::chunk_tables::ChunkTables *>(
2754  saved_states[i]->state);
2755  break;
2757  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 2)\n");
2758  delete static_cast<compat::chunk_tables_v2::ChunkTables *>(
2759  saved_states[i]->state);
2760  break;
2762  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 3)\n");
2763  delete static_cast<compat::chunk_tables_v3::ChunkTables *>(
2764  saved_states[i]->state);
2765  break;
2767  SendMsg2Socket(fd_progress, "Releasing chunk tables\n");
2768  delete static_cast<ChunkTables *>(saved_states[i]->state);
2769  break;
2771  SendMsg2Socket(fd_progress, "Releasing saved inode generation info\n");
2772  delete static_cast<cvmfs::InodeGenerationInfo *>(
2773  saved_states[i]->state);
2774  break;
2777  fd_progress, saved_states[i]->state);
2778  break;
2780  SendMsg2Socket(fd_progress, "Releasing open files counter\n");
2781  delete static_cast<uint32_t *>(saved_states[i]->state);
2782  break;
2783  default:
2784  break;
2785  }
2786  }
2787 }
2788 
2789 
2790 static void __attribute__((constructor)) LibraryMain() {
2791  g_cvmfs_exports = new loader::CvmfsExports();
2792  g_cvmfs_exports->so_version = PACKAGE_VERSION;
2793  g_cvmfs_exports->fnAltProcessFlavor = AltProcessFlavor;
2794  g_cvmfs_exports->fnInit = Init;
2795  g_cvmfs_exports->fnSpawn = Spawn;
2796  g_cvmfs_exports->fnFini = Fini;
2797  g_cvmfs_exports->fnGetErrorMsg = GetErrorMsg;
2798  g_cvmfs_exports->fnMaintenanceMode = MaintenanceMode;
2799  g_cvmfs_exports->fnSaveState = SaveState;
2800  g_cvmfs_exports->fnRestoreState = RestoreState;
2801  g_cvmfs_exports->fnFreeSavedState = FreeSavedState;
2802  cvmfs::SetCvmfsOperations(&g_cvmfs_exports->cvmfs_operations);
2803 }
2804 
2805 
2806 static void __attribute__((destructor)) LibraryExit() {
2807  delete g_cvmfs_exports;
2808  g_cvmfs_exports = NULL;
2809 }
bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
Definition: glue_buffer.h:1025
perf::Counter * n_eio_total()
Definition: mountpoint.h:234
bool cache_symlinks()
Definition: mountpoint.h:513
VfsPutRaii GetVfsPutRaii()
Definition: glue_buffer.h:688
std::string repository_name
Definition: loader.h:177
OptionsManager * options_mgr()
Definition: mountpoint.h:243
#define LogCvmfs(source, mask,...)
Definition: logging.h:25
uint32_t version
Definition: loader.h:172
bool IsCaching()
Definition: fuse_remount.h:60
void Dec(class Counter *counter)
Definition: statistics.h:49
NfsMaps * nfs_maps()
Definition: mountpoint.h:231
bool IsExternalFile() const
std::string mount_point
Definition: loader.h:178
perf::Counter * n_eio_05()
Definition: mountpoint.h:239
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
void UnregisterQuotaListener()
Definition: cvmfs.cc:1999
static const time_t kIndefiniteDeadline
Definition: mountpoint.h:482
perf::Counter * n_fs_inode_replace()
Definition: mountpoint.h:219
virtual std::string GetValue()
Definition: cvmfs.cc:2051
int64_t Xadd(class Counter *counter, const int64_t delta)
Definition: statistics.h:51
static void cvmfs_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:724
std::string ListKeysPosix(const std::string &merge_with) const
Definition: xattr.cc:137
perf::Counter * n_eio_01()
Definition: mountpoint.h:235
FileSystem * file_system()
Definition: mountpoint.h:509
void TryFinish(const shash::Any &root_hash=shash::Any())
bool InsertNegative(const shash::Md5 &hash)
Definition: lru_md.h:129
Log2Histogram * hist_fs_opendir()
Definition: mountpoint.h:210
time_t catalogs_valid_until_
Definition: cvmfs.cc:2044
static bool HasDifferentContent(const catalog::DirectoryEntry &dirent, const shash::Any &hash, const struct stat &info)
Definition: cvmfs.cc:234
virtual void ParsePath(const std::string &config_file, const bool external)=0
void Enter()
Definition: fence.h:34
bool ReplaceInode(uint64_t old_inode, const InodeEx &new_inode)
Definition: glue_buffer.h:738
static void cvmfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
Definition: cvmfs.cc:1323
time_t mtime() const
static double GetKcacheTimeout()
Definition: cvmfs.cc:182
void EnterMaintenanceMode()
void set_inode(const inode_t inode)
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:129
loader::Failures boot_status()
Definition: mountpoint.h:77
static void FreeSavedState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2712
bool IsDirectory() const
static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino)
Definition: cvmfs.cc:1584
void set_boot_status(loader::Failures code)
Definition: mountpoint.h:84
static void DoTraceInode(const int event, fuse_ino_t ino, const std::string &msg)
Definition: cvmfs.cc:457
cvmfs::Fetcher * fetcher()
Definition: mountpoint.h:505
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:182
void ShareBuffer(Item **duplicate, bool *large_alloc)
Definition: bigvector.h:81
static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:988
bool IsChunkedFile() const
FuseRemounter * fuse_remounter_
Definition: cvmfs.cc:128
uid_t talk_socket_uid()
Definition: mountpoint.h:531
int RestoreState(const int fd_progress, void *state)
Definition: cache.cc:194
void Register(const std::string &name, BaseMagicXattr *magic_xattr)
Definition: magic_xattr.cc:135
SmallHashDynamic< uint64_t, uint64_t > handle2uniqino
Definition: file_chunk.h:119
bool Insert(const fuse_ino_t &inode, const catalog::DirectoryEntry &dirent)
Definition: lru_md.h:51
google::dense_hash_map< uint64_t, DirectoryListing, hash_murmur< uint64_t > > DirectoryHandles
Definition: cvmfs.cc:156
#define PANIC(...)
Definition: exception.h:29
void Spawn()
Definition: tracer.cc:212
SpecialDirents GetSpecial() const
bool fuse_expire_entry()
Definition: mountpoint.h:514
virtual void Spawn()=0
static int AltProcessFlavor(int argc, char **argv)
Definition: cvmfs.cc:2423
uint64_t size() const
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:249
double kcache_timeout_sec()
Definition: mountpoint.h:520
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:61
static void cvmfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
Definition: cvmfs.cc:612
std::string PrintInodeGeneration()
Definition: cvmfs.cc:205
zlib::Algorithms compression_alg
Definition: file_chunk.h:71
static void SetupLoggingStandalone(const OptionsManager &options_mgr, const std::string &prefix)
Definition: mountpoint.cc:861
int MainResolveProxyDescription(int argc, char **argv)
Definition: wpad.cc:271
bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name)
Definition: glue_buffer.h:716
perf::Counter * n_fs_readlink()
Definition: mountpoint.h:224
std::string fqrn() const
Definition: mountpoint.h:507
unsigned LookupOptions
Definition: catalog_mgr.h:40
void SetMountpoint(const string &mountpoint)
Definition: auto_umount.cc:28
void InvalidateDentry(uint64_t parent_ino, const NameString &name)
Definition: fuse_remount.h:70
void set_symlink(const LinkString &symlink)
static void cvmfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:1502
BaseMagicXattr * GetLocked(const std::string &name, PathString path, catalog::DirectoryEntry *d)
Definition: magic_xattr.cc:119
Watchdog * watchdog_
Definition: cvmfs.cc:127
bool Lookup(const shash::Md5 &hash, catalog::DirectoryEntry *dirent, bool update_lru=true)
Definition: lru_md.h:136
lru::InodeCache * inode_cache()
Definition: mountpoint.h:519
NotificationClient * notification_client_
Definition: cvmfs.cc:126
void * SaveState(const int fd_progress)
Definition: cache.cc:221
DirectoryHandles * directory_handles_
Definition: cvmfs.cc:157
StateId state_id
Definition: loader.h:122
CVMFS_EXPORT void CleanupLibcryptoMt()
Definition: crypto_util.cc:70
const shash::Any & content_hash() const
Definition: file_chunk.h:41
time_t catalogs_valid_until()
Definition: fuse_remount.h:68
static bool SaveState(const int fd_progress, loader::StateList *saved_states)
Definition: cvmfs.cc:2448
inode_t inode() const
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:656
bool ListingStat(const PathString &path, StatEntryList *listing)
assert((mem||(size==0))&&"Out Of Memory")
void ** fuse_channel_or_session
Definition: loader.h:195
MountPoint * mount_point_
Definition: cvmfs.cc:124
MagicXattrManager * magic_xattr_mgr()
Definition: mountpoint.h:510
std::string program_name
Definition: loader.h:180
Log2Histogram * hist_fs_read()
Definition: mountpoint.h:214
bool IsDirectIo() const
bool SendFd2Socket(int socket_fd, int passing_fd)
Definition: posix.cc:665
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
void EnableFuseExpireEntry()
Definition: mountpoint.cc:1234
static bool RestoreState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2545
virtual uint64_t GetSize()=0
perf::Counter * n_fs_read()
Definition: mountpoint.h:223
bool Lookup(const fuse_ino_t &inode, catalog::DirectoryEntry *dirent, bool update_lru=true)
Definition: lru_md.h:59
void UmountOnCrash()
Definition: auto_umount.cc:38
int fd
Definition: file_chunk.h:82
lru::Md5PathCache * md5path_cache()
Definition: mountpoint.h:521
shash::Any checksum() const
void ParseDefault(const std::string &fqrn)
Definition: options.cc:282
SmallHashDynamic< uint64_t, ChunkFd > handle2fd
Definition: file_chunk.h:120
bool VfsPut(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:616
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:110
bool has_membership_req()
Definition: mountpoint.h:511
virtual bool PrepareValueFenced()
Definition: cvmfs.cc:2046
virtual std::string GetValue()
Definition: cvmfs.cc:2062
unsigned int mode() const
struct cvmcache_object_info __attribute__
Definition: atomic.h:24
const unsigned kLookupDefault
Definition: catalog_mgr.h:41
void RemapCatalogFd(int from, int to)
Definition: mountpoint.cc:1137
bool Pin(const string &path)
Definition: cvmfs.cc:1831
static void cvmfs_destroy(void *unused __attribute__((unused)))
Definition: cvmfs.cc:1965
perf::Counter * n_eio_08()
Definition: mountpoint.h:242
static TalkManager * Create(const std::string &socket_path, MountPoint *mount_point, FuseRemounter *remounter)
Definition: talk.cc:78
unsigned max_open_files_
Definition: cvmfs.cc:161
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:53
perf::Counter * n_fs_open()
Definition: mountpoint.h:222
static void cvmfs_init(void *userdata, struct fuse_conn_info *conn)
Definition: cvmfs.cc:1903
TalkManager * talk_mgr_
Definition: cvmfs.cc:125
const unsigned int kMinOpenFiles
Definition: cvmfs.cc:169
virtual uint64_t GetCapacity()=0
std::string membership_req()
Definition: mountpoint.h:522
void Throttle()
Definition: backoff.cc:50
std::string GetListString(catalog::DirectoryEntry *dirent)
Definition: magic_xattr.cc:77
bool Lookup(const fuse_ino_t &inode, PathString *path, bool update_lru=true)
Definition: lru_md.h:92
uint64_t next_directory_handle_
Definition: cvmfs.cc:159
void Unlock()
Definition: file_chunk.h:106
static void InitOptionsMgr(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2141
Fence * fence()
Definition: fuse_remount.h:67
const uint64_t cache_timeout()
Definition: mountpoint.h:452
pid_t pid_
Definition: cvmfs.cc:149
void DisableCacheSymlinks()
Definition: mountpoint.cc:1226
PathString path
Definition: file_chunk.h:70
static bool GetDirentForInode(const fuse_ino_t ino, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:281
glue::PageCacheTracker * page_cache_tracker()
Definition: mountpoint.h:524
NameString name() const
static Watchdog * Create(FnOnCrash on_crash)
Definition: monitor.cc:61
bool IsLink() const
void SetBit(unsigned int bit, T *field)
Definition: algorithm.h:30
perf::Counter * n_eio_07()
Definition: mountpoint.h:241
BigVector< FileChunk > FileChunkList
Definition: file_chunk.h:51
virtual void Spawn()=0
static bool FixupOpenInode(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:258
bool HasXattrs() const
virtual std::string GetValue()
Definition: cvmfs.cc:2080
static void TraceInode(const int event, fuse_ino_t ino, const std::string &msg)
Definition: cvmfs.cc:472
void GetLimitNoFile(unsigned *soft_limit, unsigned *hard_limit)
Definition: posix.cc:1477
bool Get(const std::string &key, std::string *value) const
Definition: xattr.cc:108
catalog::ClientCatalogManager * catalog_mgr()
Definition: mountpoint.h:496
void Set(const int64_t val)
Definition: statistics.h:33
Log2Histogram * hist_fs_forget_multi()
Definition: mountpoint.h:207
static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
Definition: cvmfs.cc:1741
bool IsRegular() const
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:290
void ClearBit(unsigned int bit, T *field)
Definition: algorithm.h:35
void DoubleCapacity()
Definition: bigvector.h:87
bool platform_getxattr(const std::string &path, const std::string &name, std::string *value)
static bool MayBeInPageCacheTracker(const catalog::DirectoryEntry &dirent)
Definition: cvmfs.cc:229
Log2Histogram * hist_fs_release()
Definition: mountpoint.h:215
pthread_mutex_t * Handle2Lock(const uint64_t handle) const
Definition: file_chunk.cc:141
void Lock()
Definition: file_chunk.h:101
uint64_t FindInode(const PathString &path)
Definition: glue_buffer.h:708
ListenerHandle * RegisterWatchdogListener(QuotaManager *quota_manager, const string &repository_name)
const loader::LoaderExports * loader_exports_
Definition: cvmfs.cc:147
EVisibility visibility()
Definition: magic_xattr.h:198
virtual inode_t GetGeneration()=0
std::string config_files
Definition: loader.h:179
perf::Counter * no_open_dirs()
Definition: mountpoint.h:232
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:515
Log2Histogram * hist_fs_releasedir()
Definition: mountpoint.h:211
quota::ListenerHandle * watchdog_listener_
Definition: cvmfs.cc:150
void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s)
Definition: glue_buffer.h:854
virtual bool IsCanceled()
Definition: cvmfs.cc:176
void SetSize(const size_t new_size)
Definition: bigvector.h:116
std::string boot_error()
Definition: mountpoint.h:78
off_t offset() const
Definition: file_chunk.h:42
lru::PathCache * path_cache()
Definition: mountpoint.h:525
struct statvfs * info()
Definition: mountpoint.h:453
virtual std::string GetValue()
Definition: cvmfs.cc:2070
static void cvmfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:1085
zlib::Algorithms compression_algorithm() const
bool IsNfsSource()
Definition: mountpoint.h:195
CacheManager * cache_mgr()
Definition: mountpoint.h:201
const signature::SignatureManager * signature_mgr() const
Definition: repository.h:121
file_watcher::FileWatcher * resolv_conf_watcher()
Definition: mountpoint.h:502
unsigned chunk_idx
Definition: file_chunk.h:83
virtual bool Pin(const shash::Any &hash, const uint64_t size, const std::string &description, const bool is_catalog)=0
bool IsMemberOf(const pid_t pid, const std::string &membership)
virtual std::string GetValue()
Definition: cvmfs.cc:2076
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:145
void SpawnCleaner(unsigned interval_s)
Definition: glue_buffer.cc:170
static void ShutdownMountpoint()
Definition: cvmfs.cc:2375
static bool AssertOrLog(int t, const LogSource, const int, const char *,...)
Definition: exception.h:61
unsigned FindChunkIdx(const uint64_t offset)
Definition: file_chunk.cc:23
perf::TelemetryAggregator * telemetry_aggr()
Definition: mountpoint.h:529
bool SendFuseFd(const std::string &socket_path)
Definition: cvmfs.cc:2010
perf::Counter * no_open_files()
Definition: mountpoint.h:233
AuthzSessionManager * authz_session_mgr()
Definition: mountpoint.h:494
LinkString symlink() const
void Insert(const Key &key, const Value &value)
Definition: smallhash.h:109
IoErrorInfo * io_error_info()
Definition: mountpoint.h:229
download::DownloadManager * download_mgr()
Definition: mountpoint.h:498
perf::Counter * n_fs_stat_stale()
Definition: mountpoint.h:226
static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations)
Definition: cvmfs.cc:1973
bool simple_options_parsing
Definition: loader.h:188
perf::Counter * n_fs_forget()
Definition: mountpoint.h:218
Log2Histogram * hist_fs_readlink()
Definition: mountpoint.h:209
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2187
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:80
ListenerHandle * RegisterUnpinListener(QuotaManager *quota_manager, CatalogManager *catalog_manager, const string &repository_name)
string StringifyInt(const int64_t value)
Definition: string.cc:78
SmallHashDynamic< uint64_t, uint32_t > inode2references
Definition: file_chunk.h:125
void Leave()
Definition: fence.h:41
uint64_t platform_monotonic_time()
static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino)
Definition: cvmfs.cc:793
size_t capacity() const
Definition: bigvector.h:122
static void ReplyNegative(const catalog::DirectoryEntry &dirent, fuse_req_t req)
Definition: cvmfs.cc:700
void Inc(class Counter *counter)
Definition: statistics.h:50
virtual int Close(int fd)=0
bool GetValue(const std::string &key, std::string *value) const
Definition: options.cc:376
static void cvmfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
Definition: cvmfs.cc:1037
ChunkTables * chunk_tables()
Definition: mountpoint.h:497
const unsigned kLookupRawSymlink
Definition: catalog_mgr.h:42
pthread_mutex_t lock_directory_handles_
Definition: cvmfs.cc:158
static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:852
OptionsManager * options_mgr_
Definition: cvmfs.cc:148
virtual bool GetPath(const uint64_t inode, PathString *path)=0
shash::Algorithms hash_algorithm() const
glue::DentryTracker * dentry_tracker()
Definition: mountpoint.h:523
static int MainCacheManager(int argc, char **argv)
Definition: quota_posix.cc:950
perf::Counter * n_fs_dir_open()
Definition: mountpoint.h:217
Log2Histogram * hist_fs_readdir()
Definition: mountpoint.h:212
cvmfs::Fetcher * external_fetcher()
Definition: mountpoint.h:508
bool enforce_acls()
Definition: mountpoint.h:512
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
static bool CheckVoms(const fuse_ctx &fctx)
Definition: cvmfs.cc:216
static void FillOpenFlags(const glue::PageCacheTracker::OpenDirectives od, struct fuse_file_info *fi)
Definition: cvmfs.cc:1061
void FreeState(const int fd_progress, void *state)
Definition: cache.cc:100
static FileSystem * Create(const FileSystemInfo &fs_info)
Definition: mountpoint.cc:165
bool IsEmpty() const
Definition: bigvector.h:72
bool Contains(const Key &key) const
Definition: smallhash.h:102
Log2Histogram * hist_fs_open()
Definition: mountpoint.h:213
OpenDirectives Open(uint64_t inode, const shash::Any &hash, const struct stat &info)
Definition: glue_buffer.cc:317
pthread_mutex_t * lock()
Definition: mountpoint.h:454
std::string ToString() const
Definition: shortstring.h:141
QuotaManager * quota_mgr()
Definition: cache.h:198
static string GetErrorMsg()
Definition: cvmfs.cc:2363
static uint64_t GetDirentForPath(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:372
int ConnectSocket(const std::string &path)
Definition: posix.cc:424
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:274
virtual void Spawn()
Definition: nfs_maps.h:36
Log2Histogram * hist_fs_lookup()
Definition: mountpoint.h:205
bool IsCompatibleFileType(unsigned mode) const
Definition: glue_buffer.h:85
bool TestBit(unsigned int bit, const T field)
Definition: algorithm.h:40
CVMFS_EXPORT void SetupLibcryptoMt()
Definition: crypto_util.cc:52
void Close(uint64_t inode)
Definition: glue_buffer.cc:399
string * g_boot_error
Definition: cvmfs.cc:2034
bool ListFileChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks)
static void Fini()
Definition: cvmfs.cc:2404
perf::Counter * n_fs_lookup()
Definition: mountpoint.h:220
static void Spawn()
Definition: cvmfs.cc:2312
static bool UseWatchdog()
Definition: cvmfs.cc:195
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:80
OpenDirectives OpenDirect()
Definition: glue_buffer.cc:388
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:217
uint32_t size() const
Definition: smallhash.h:302
uint64_t next_handle
Definition: file_chunk.h:126
shash::Any HashChunkList()
Definition: file_chunk.cc:49
size_t size() const
Definition: file_chunk.h:43
FuseInterruptCue(fuse_req_t *r)
Definition: cvmfs.cc:174
void UnregisterListener(ListenerHandle *handle)
Definition: mutex.h:42
bool Evict(const string &path)
Definition: cvmfs.cc:1804
std::vector< SavedState * > StateList
Definition: loader.h:125
perf::Counter * n_eio_02()
Definition: mountpoint.h:236
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
static unsigned CheckMaxOpenFiles()
Definition: cvmfs.cc:2161
FileSystem * file_system_
Definition: cvmfs.cc:123
perf::Counter * n_eio_03()
Definition: mountpoint.h:237
EvictRaii GetEvictRaii()
Definition: glue_buffer.h:1040
perf::Counter * n_eio_04()
Definition: mountpoint.h:238
void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode)
Definition: cvmfs.cc:189
bool Erase(const Key &key)
Definition: smallhash.h:115
bool LookupXattrs(const PathString &path, XattrList *xattrs)
perf::Counter * n_fs_lookup_negative()
Definition: mountpoint.h:221
StatfsCache * statfs_cache()
Definition: mountpoint.h:536
virtual uint64_t GetInode(const PathString &path)=0
void Spawn(const std::string &crash_dump_path)
Definition: monitor.cc:511
Log2Histogram * hist_fs_forget()
Definition: mountpoint.h:206
const int kNumReservedFd
Definition: cvmfs.cc:165
static void ReplyBufferSlice(const fuse_req_t req, const char *buffer, const size_t buffer_size, const off_t offset, const size_t max_size)
Definition: cvmfs.cc:1021
unsigned GetLength() const
Definition: shortstring.h:131
virtual void IncGeneration(const uint64_t by)=0
bool Lookup(const Key &key, Value *value) const
Definition: smallhash.h:73
inode_t MangleInode(const inode_t inode) const
Definition: catalog_mgr.h:202
bool Insert(const fuse_ino_t &inode, const PathString &path)
Definition: lru_md.h:84
BackoffThrottle * backoff_throttle()
Definition: mountpoint.h:495
Log2Histogram * hist_fs_getattr()
Definition: mountpoint.h:208
gid_t talk_socket_gid()
Definition: mountpoint.h:532
const char * c_str() const
Definition: shortstring.h:145
static bool GetPathForInode(const fuse_ino_t ino, PathString *path)
Definition: cvmfs.cc:423
static const unsigned int kBitDirectIo
Definition: glue_buffer.h:950
const char * GetChars() const
Definition: shortstring.h:123
static void RegisterMagicXattrs()
Definition: cvmfs.cc:2091
bool IsDefined(const std::string &key)
Definition: options.cc:370
std::string GetCurrentWorkingDirectory()
Definition: posix.cc:1057
virtual void Remove(const shash::Any &file)=0
glue::InodeTracker * inode_tracker()
Definition: mountpoint.h:518
bool Insert(const shash::Md5 &hash, const catalog::DirectoryEntry &dirent)
Definition: lru_md.h:121
perf::Counter * n_fs_stat()
Definition: mountpoint.h:225
static void size_t size
Definition: smalloc.h:54
virtual ~FuseInterruptCue()
Definition: cvmfs.cc:175
static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
Definition: cvmfs.cc:484
#define ENOATTR
Definition: cvmfs.cc:25
virtual int64_t Pread(int fd, void *buf, uint64_t size, uint64_t offset)=0
SmallHashDynamic< uint64_t, FileChunkReflist > inode2chunks
Definition: file_chunk.h:124
static bool MaintenanceMode(const int fd_progress)
Definition: cvmfs.cc:2434
uint64_t * expiry_deadline()
Definition: mountpoint.h:451
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:228
FileChunkList * list
Definition: file_chunk.h:69
static FileSystem * InitSystemFs(const string &mount_path, const string &fqrn, FileSystem::FileSystemInfo fs_info)
Definition: cvmfs.cc:2106
void VfsGet(const InodeEx inode_ex, const PathString &path)
Definition: glue_buffer.h:684
const Item * AtPtr(const size_t index) const
Definition: bigvector.h:55
static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
Definition: cvmfs.cc:1650
Tracer * tracer()
Definition: mountpoint.h:534
perf::Counter * n_fs_statfs_cached()
Definition: mountpoint.h:228
bool FindPath(InodeEx *inode_ex, PathString *path)
Definition: glue_buffer.h:690
struct stat GetStatStructure() const
virtual bool HasCapability(Capabilities capability)=0
static void AddToDirListing(const fuse_req_t req, const char *name, const struct stat *stat_info, BigVector< char > *listing)
Definition: cvmfs.cc:825
size_t size() const
Definition: bigvector.h:121
download::DownloadManager * external_download_mgr()
Definition: mountpoint.h:499
int Fetch(const shash::Any &id, const uint64_t size, const std::string &name, const zlib::Algorithms compression_algorithm, const CacheManager::ObjectType object_type, const std::string &alt_url="", off_t range_offset=-1)
Definition: fetch.cc:82
perf::Counter * n_fs_statfs()
Definition: mountpoint.h:227
quota::ListenerHandle * unpin_listener_
Definition: cvmfs.cc:151
static MountPoint * Create(const std::string &fqrn, FileSystem *file_system, OptionsManager *options_mgr=NULL)
Definition: mountpoint.cc:1243
void Spawn()
Definition: talk.cc:843
fuse_req_t * req_ptr_
Definition: cvmfs.cc:178
perf::Counter * n_eio_06()
Definition: mountpoint.h:240
OptionsManager * options_mgr
Definition: mountpoint.h:139