CernVM-FS  2.13.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 // clang-format off
33 #include <sys/xattr.h> // NOLINT
34 // clang-format on
35 
36 
37 #include "cvmfs.h"
38 
39 #include <dirent.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <inttypes.h>
43 #include <pthread.h>
44 #include <stddef.h>
45 #include <stdint.h>
46 #include <sys/errno.h>
47 #include <sys/file.h>
48 #include <sys/mount.h>
49 #include <sys/resource.h>
50 #include <sys/stat.h>
51 #include <sys/time.h>
52 #include <sys/types.h>
53 #include <sys/wait.h>
54 #include <unistd.h>
55 
56 #include <algorithm>
57 #include <cassert>
58 #include <cstdio>
59 #include <cstdlib>
60 #include <cstring>
61 #include <ctime>
62 #include <functional>
63 #include <google/dense_hash_map>
64 #include <map>
65 #include <new>
66 #include <string>
67 #include <utility>
68 #include <vector>
69 
70 #include "authz/authz_session.h"
71 #include "auto_umount.h"
72 #include "backoff.h"
73 #include "cache.h"
74 #include "cache_posix.h"
75 #include "cache_stream.h"
76 #include "catalog_mgr_client.h"
77 #include "clientctx.h"
78 #include "compat.h"
80 #include "crypto/crypto_util.h"
81 #include "crypto/hash.h"
82 #include "crypto/signature.h"
83 #include "directory_entry.h"
84 #include "duplex_fuse.h"
85 #include "fence.h"
86 #include "fetch.h"
87 #include "file_chunk.h"
88 #include "fuse_inode_gen.h"
89 #include "fuse_remount.h"
90 #include "globals.h"
91 #include "glue_buffer.h"
92 #include "history_sqlite.h"
93 #include "interrupt.h"
94 #include "loader.h"
95 #include "lru_md.h"
96 #include "magic_xattr.h"
97 #include "manifest_fetch.h"
98 #include "monitor.h"
99 #include "mountpoint.h"
100 #include "network/download.h"
101 #include "nfs_maps.h"
102 #include "notification_client.h"
103 #include "options.h"
104 #include "quota_listener.h"
105 #include "quota_posix.h"
106 #include "sanitizer.h"
107 #include "shortstring.h"
108 #include "sqlitemem.h"
109 #include "sqlitevfs.h"
110 #include "statistics.h"
111 #include "talk.h"
112 #include "telemetry_aggregator.h"
113 #include "tracer.h"
114 #include "util/algorithm.h"
115 #include "util/atomic.h"
116 #include "util/concurrency.h"
117 #include "util/exception.h"
118 #include "util/logging.h"
119 #include "util/platform.h"
120 #include "util/smalloc.h"
121 #include "util/testing.h"
122 #include "util/uuid.h"
123 #include "wpad.h"
124 #include "xattr.h"
125 
126 using namespace std; // NOLINT
127 
128 namespace cvmfs {
129 
137 
138 
144  char *buffer;
146  // Not really used anymore. But directory listing needs to be migrated during
147  // hotpatch. If buffer is allocated by smmap, capacity is zero.
148  size_t size;
149  size_t capacity;
150 
151  DirectoryListing() : buffer(NULL), size(0), capacity(0) { }
152 };
153 
156 pid_t pid_ = 0;
159 
160 
161 typedef google::dense_hash_map<uint64_t, DirectoryListing,
165 pthread_mutex_t lock_directory_handles_ = PTHREAD_MUTEX_INITIALIZER;
167 
168 unsigned max_open_files_;
174 bool check_fd_overflow_ = true;
178 const int kNumReservedFd = 512;
182 const unsigned int kMinOpenFiles = 8192;
183 
184 
186  public:
187  explicit FuseInterruptCue(fuse_req_t *r) : req_ptr_(r) { }
188  virtual ~FuseInterruptCue() { }
189  virtual bool IsCanceled() { return fuse_req_interrupted(*req_ptr_); }
190 
191  private:
192  fuse_req_t *req_ptr_;
193 };
194 
201 struct FuseState {
202  FuseState() : version(0), cache_symlinks(false), has_dentry_expire(false) { }
203  unsigned version;
206 };
207 
208 
214 static inline bool IncAndCheckNoOpenFiles() {
215  const int64_t no_open_files = perf::Xadd(file_system_->no_open_files(), 1);
216  if (!check_fd_overflow_)
217  return true;
218  return no_open_files < (static_cast<int>(max_open_files_) - kNumReservedFd);
219 }
220 
221 static inline double GetKcacheTimeout() {
222  if (!fuse_remounter_->IsCaching())
223  return 0.0;
225 }
226 
227 
228 void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode) {
229  *drainout_mode = fuse_remounter_->IsInDrainoutMode();
230  *maintenance_mode = fuse_remounter_->IsInMaintenanceMode();
231 }
232 
233 
234 static bool UseWatchdog() {
235  if (loader_exports_ == NULL || loader_exports_->version < 2) {
236  return true; // spawn watchdog by default
237  // Note: with library versions before 2.1.8 it might not
238  // create stack traces properly in all cases
239  }
240 
242 }
243 
244 std::string PrintInodeGeneration() {
245  return "init-catalog-revision: "
247  + "current-catalog-revision: "
249  + "incarnation: " + StringifyInt(inode_generation_info_.incarnation)
250  + " " + "inode generation: "
252 }
253 
254 
255 static bool CheckVoms(const fuse_ctx &fctx) {
257  return true;
258  string mreq = mount_point_->membership_req();
260  "Got VOMS authz %s from filesystem "
261  "properties",
262  mreq.c_str());
263 
264  if (fctx.uid == 0)
265  return true;
266 
267  return mount_point_->authz_session_mgr()->IsMemberOf(fctx.pid, mreq);
268 }
269 
271  return dirent.IsRegular()
272  && (dirent.inode() < mount_point_->catalog_mgr()->GetRootInode());
273 }
274 
276  const shash::Any &hash,
277  const struct stat &info) {
278  if (hash == dirent.checksum())
279  return false;
280  // For chunked files, we don't want to load the full list of chunk hashes
281  // so we only check the last modified timestamp
282  if (dirent.IsChunkedFile() && (info.st_mtime == dirent.mtime()))
283  return false;
284  return true;
285 }
286 
297 static bool FixupOpenInode(const PathString &path,
298  catalog::DirectoryEntry *dirent) {
299  if (!MayBeInPageCacheTracker(*dirent))
300  return false;
301 
302  CVMFS_TEST_INJECT_BARRIER("_CVMFS_TEST_BARRIER_INODE_REPLACE");
303 
304  const bool is_stale = mount_point_->page_cache_tracker()->IsStale(*dirent);
305 
306  if (is_stale) {
307  // Overwrite dirent with inode from current generation
308  const bool found = mount_point_->catalog_mgr()->LookupPath(
309  path, catalog::kLookupDefault, dirent);
310  assert(found);
311  }
312 
313  return is_stale;
314 }
315 
316 static bool GetDirentForInode(const fuse_ino_t ino,
317  catalog::DirectoryEntry *dirent) {
318  // Lookup inode in cache
319  if (mount_point_->inode_cache()->Lookup(ino, dirent))
320  return true;
321 
322  // Look in the catalogs in 2 steps: lookup inode->path, lookup path
323  static catalog::DirectoryEntry dirent_negative = catalog::DirectoryEntry(
325  // Reset directory entry. If the function returns false and dirent is no
326  // the kDirentNegative, it was an I/O error
327  *dirent = catalog::DirectoryEntry();
328 
330 
331  if (file_system_->IsNfsSource()) {
332  // NFS mode
333  PathString path;
334  bool retval = file_system_->nfs_maps()->GetPath(ino, &path);
335  if (!retval) {
336  *dirent = dirent_negative;
337  return false;
338  }
339  if (catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent)) {
340  // Fix inodes
341  dirent->set_inode(ino);
342  mount_point_->inode_cache()->Insert(ino, *dirent);
343  return true;
344  }
345  return false; // Not found in catalog or catalog load error
346  }
347 
348  // Non-NFS mode
349  PathString path;
350  if (ino == catalog_mgr->GetRootInode()) {
351  bool retval = catalog_mgr->LookupPath(PathString(), catalog::kLookupDefault,
352  dirent);
353 
355  "GetDirentForInode: Race condition? Not found dirent %s",
356  dirent->name().c_str())) {
357  return false;
358  }
359 
360  dirent->set_inode(ino);
361  mount_point_->inode_cache()->Insert(ino, *dirent);
362  return true;
363  }
364 
366  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, &path);
367  if (!retval) {
368  // This may be a retired inode whose stat information is only available
369  // in the page cache tracker because there is still an open file
371  "GetDirentForInode inode lookup failure %" PRId64, ino);
372  *dirent = dirent_negative;
373  // Indicate that the inode was not found in the tracker rather than not
374  // found in the catalog
375  dirent->set_inode(ino);
376  return false;
377  }
378  if (catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent)) {
379  if (!inode_ex.IsCompatibleFileType(dirent->mode())) {
381  "Warning: inode %" PRId64 " (%s) changed file type", ino,
382  path.c_str());
383  // TODO(jblomer): we detect this issue but let it continue unhandled.
384  // Fix me.
385  }
386 
387  // Fix inodes
388  dirent->set_inode(ino);
389  mount_point_->inode_cache()->Insert(ino, *dirent);
390  return true;
391  }
392 
393  // Can happen after reload of catalogs or on catalog load failure
394  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForInode path lookup failure");
395  return false;
396 }
397 
398 
406 static uint64_t GetDirentForPath(const PathString &path,
407  catalog::DirectoryEntry *dirent) {
408  uint64_t live_inode = 0;
409  if (!file_system_->IsNfsSource())
410  live_inode = mount_point_->inode_tracker()->FindInode(path);
411 
413  "GetDirentForPath: live inode for %s: %" PRIu64, path.c_str(),
414  live_inode);
415 
416  shash::Md5 md5path(path.GetChars(), path.GetLength());
417  if (mount_point_->md5path_cache()->Lookup(md5path, dirent)) {
418  if (dirent->GetSpecial() == catalog::kDirentNegative)
419  return false;
420  // We may have initially stored the entry with an old inode in the
421  // md5path cache and now should update it with the new one.
422  if (!file_system_->IsNfsSource() && (live_inode != 0))
423  dirent->set_inode(live_inode);
424  return 1;
425  }
426 
428 
429  // Lookup inode in catalog TODO: not twice md5 calculation
430  bool retval;
431  retval = catalog_mgr->LookupPath(path, catalog::kLookupDefault, dirent);
432  if (retval) {
433  if (file_system_->IsNfsSource()) {
434  dirent->set_inode(file_system_->nfs_maps()->GetInode(path));
435  } else if (live_inode != 0) {
436  dirent->set_inode(live_inode);
437  if (FixupOpenInode(path, dirent)) {
439  "content of %s change, replacing inode %" PRIu64
440  " --> %" PRIu64,
441  path.c_str(), live_inode, dirent->inode());
442  return live_inode;
443  // Do not populate the md5path cache until the inode tracker is fixed
444  }
445  }
446  mount_point_->md5path_cache()->Insert(md5path, *dirent);
447  return 1;
448  }
449 
450  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry");
451  // Only insert ENOENT results into negative cache. Otherwise it was an
452  // error loading nested catalogs
453  if (dirent->GetSpecial() == catalog::kDirentNegative)
455  return 0;
456 }
457 
458 
459 static bool GetPathForInode(const fuse_ino_t ino, PathString *path) {
460  // Check the path cache first
461  if (mount_point_->path_cache()->Lookup(ino, path))
462  return true;
463 
464  if (file_system_->IsNfsSource()) {
465  // NFS mode, just a lookup
466  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %lu - lookup in NFS maps", ino);
467  if (file_system_->nfs_maps()->GetPath(ino, path)) {
468  mount_point_->path_cache()->Insert(ino, *path);
469  return true;
470  }
471  return false;
472  }
473 
474  if (ino == mount_point_->catalog_mgr()->GetRootInode())
475  return true;
476 
477  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %lu - looking in inode tracker", ino);
479  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, path);
480 
482  "GetPathForInode: Race condition? "
483  "Inode not found in inode tracker at path %s",
484  path->c_str())) {
485  return false;
486  }
487 
488 
489  mount_point_->path_cache()->Insert(ino, *path);
490  return true;
491 }
492 
493 static void DoTraceInode(const int event,
494  fuse_ino_t ino,
495  const std::string &msg) {
496  PathString path;
497  bool found = GetPathForInode(ino, &path);
498  if (!found) {
500  "Tracing: Could not find path for inode %" PRIu64, uint64_t(ino));
501  mount_point_->tracer()->Trace(event, PathString("@UNKNOWN"), msg);
502  } else {
503  mount_point_->tracer()->Trace(event, path, msg);
504  }
505 }
506 
507 static void inline TraceInode(const int event,
508  fuse_ino_t ino,
509  const std::string &msg) {
510  if (mount_point_->tracer()->IsActive())
511  DoTraceInode(event, ino, msg);
512 }
513 
519 static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
521 
523  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
524  FuseInterruptCue ic(&req);
525  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
527 
530 
531  fuse_ino_t parent_fuse = parent;
532  parent = catalog_mgr->MangleInode(parent);
534  "cvmfs_lookup in parent inode: %" PRIu64 " for name: %s",
535  uint64_t(parent), name);
536 
537  PathString path;
538  PathString parent_path;
539  uint64_t live_inode = 0;
541  struct fuse_entry_param result;
542 
543  memset(&result, 0, sizeof(result));
544  double timeout = GetKcacheTimeout();
545  result.attr_timeout = timeout;
546  result.entry_timeout = timeout;
547 
548  // Special NFS lookups: . and ..
549  if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
550  if (GetDirentForInode(parent, &dirent)) {
551  if (strcmp(name, ".") == 0) {
552  goto lookup_reply_positive;
553  } else {
554  // Lookup for ".."
555  if (dirent.inode() == catalog_mgr->GetRootInode()) {
556  dirent.set_inode(1);
557  goto lookup_reply_positive;
558  }
559  if (!GetPathForInode(parent, &parent_path))
560  goto lookup_reply_negative;
561  if (GetDirentForPath(GetParentPath(parent_path), &dirent) > 0)
562  goto lookup_reply_positive;
563  }
564  }
565  // No entry for "." or no entry for ".."
566  if (dirent.GetSpecial() == catalog::kDirentNegative)
567  goto lookup_reply_negative;
568  else
569  goto lookup_reply_error;
570  assert(false);
571  }
572 
573  if (!GetPathForInode(parent, &parent_path)) {
574  LogCvmfs(kLogCvmfs, kLogDebug, "no path for parent inode found");
575  goto lookup_reply_negative;
576  }
577 
578  path.Assign(parent_path);
579  path.Append("/", 1);
580  path.Append(name, strlen(name));
581  live_inode = GetDirentForPath(path, &dirent);
582  if (live_inode == 0) {
583  if (dirent.GetSpecial() == catalog::kDirentNegative)
584  goto lookup_reply_negative;
585  else
586  goto lookup_reply_error;
587  }
588 
589 lookup_reply_positive:
590  mount_point_->tracer()->Trace(Tracer::kEventLookup, path, "lookup()");
591  if (!file_system_->IsNfsSource()) {
592  if (live_inode > 1) {
593  // live inode is stale (open file), we replace it
594  assert(dirent.IsRegular());
595  assert(dirent.inode() != live_inode);
596 
597  // The new inode is put in the tracker with refcounter == 0
598  bool replaced = mount_point_->inode_tracker()->ReplaceInode(
599  live_inode, glue::InodeEx(dirent.inode(), dirent.mode()));
600  if (replaced)
602  }
604  glue::InodeEx(dirent.inode(), dirent.mode()), path);
605  }
606  // We do _not_ track (and evict) positive replies; among other things, test
607  // 076 fails with the following line uncommented
608  //
609  // WARNING! ENABLING THIS BREAKS ANY TYPE OF MOUNTPOINT POINTING TO THIS INODE
610  //
611  // only safe if fuse_expire_entry is available
613  || (mount_point_->cache_symlinks() && dirent.IsLink())) {
614  LogCvmfs(kLogCache, kLogDebug, "Dentry to evict: %s", name);
615  mount_point_->dentry_tracker()->Add(parent_fuse, name,
616  static_cast<uint64_t>(timeout));
617  }
618 
620  result.ino = dirent.inode();
621  result.attr = dirent.GetStatStructure();
622  fuse_reply_entry(req, &result);
623  return;
624 
625 lookup_reply_negative:
626  mount_point_->tracer()->Trace(Tracer::kEventLookup, path,
627  "lookup()-NOTFOUND");
628  // Will be a no-op if there is no fuse cache eviction
629  mount_point_->dentry_tracker()->Add(parent_fuse, name, uint64_t(timeout));
632  result.ino = 0;
633  fuse_reply_entry(req, &result);
634  return;
635 
636 lookup_reply_error:
637  mount_point_->tracer()->Trace(Tracer::kEventLookup, path,
638  "lookup()-NOTFOUND");
640 
642  "EIO (01): lookup failed for %s", name);
645 
646  fuse_reply_err(req, EIO);
647 }
648 
649 
653 static void cvmfs_forget(fuse_req_t req,
654  fuse_ino_t ino,
655 #if CVMFS_USE_LIBFUSE == 2
656  unsigned long nlookup // NOLINT
657 #else
658  uint64_t nlookup
659 #endif
660 ) {
662 
664 
665  // The libfuse high-level library does the same
666  if (ino == FUSE_ROOT_ID) {
667  fuse_reply_none(req);
668  return;
669  }
670 
671  // Ensure that we don't need to call catalog_mgr()->MangleInode(ino)
673 
674  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
675  uint64_t(ino), nlookup);
676 
677  if (!file_system_->IsNfsSource()) {
678  bool removed = mount_point_->inode_tracker()->GetVfsPutRaii().VfsPut(
679  ino, nlookup);
680  if (removed)
682  }
683 
684  fuse_reply_none(req);
685 }
686 
687 
688 #if (FUSE_VERSION >= 29)
689 static void cvmfs_forget_multi(fuse_req_t req,
690  size_t count,
691  struct fuse_forget_data *forgets) {
693 
695  if (file_system_->IsNfsSource()) {
696  fuse_reply_none(req);
697  return;
698  }
699 
700  {
702  ->GetVfsPutRaii();
704  evict_raii = mount_point_->page_cache_tracker()->GetEvictRaii();
705  for (size_t i = 0; i < count; ++i) {
706  if (forgets[i].ino == FUSE_ROOT_ID) {
707  continue;
708  }
709 
710  // Ensure that we don't need to call catalog_mgr()->MangleInode(ino)
711  assert(forgets[i].ino > mount_point_->catalog_mgr()->kInodeOffset);
712  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
713  forgets[i].ino, forgets[i].nlookup);
714 
715  bool removed = vfs_put_raii.VfsPut(forgets[i].ino, forgets[i].nlookup);
716  if (removed)
717  evict_raii.Evict(forgets[i].ino);
718  }
719  }
720 
721  fuse_reply_none(req);
722 }
723 #endif // FUSE_VERSION >= 29
724 
725 
732 static void ReplyNegative(const catalog::DirectoryEntry &dirent,
733  fuse_req_t req) {
734  if (dirent.GetSpecial() == catalog::kDirentNegative) {
735  fuse_reply_err(req, ENOENT);
736  } else {
737  const char *name = dirent.name().c_str();
738  const char *link = dirent.symlink().c_str();
739 
740  LogCvmfs(
742  "EIO (02): CVMFS-specific metadata not found for name=%s symlink=%s",
743  name ? name : "<unset>", link ? link : "<unset>");
744 
747  fuse_reply_err(req, EIO);
748  }
749 }
750 
751 
755 static void cvmfs_getattr(fuse_req_t req, fuse_ino_t ino,
756  struct fuse_file_info *fi) {
758 
760  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
761  FuseInterruptCue ic(&req);
762  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
764 
766  ino = mount_point_->catalog_mgr()->MangleInode(ino);
767  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for inode: %" PRIu64,
768  uint64_t(ino));
769 
770  if (!CheckVoms(*fuse_ctx)) {
772  fuse_reply_err(req, EACCES);
773  return;
774  }
776  bool found = GetDirentForInode(ino, &dirent);
777  TraceInode(Tracer::kEventGetAttr, ino, "getattr()");
778  if ((!found && (dirent.inode() == ino)) || MayBeInPageCacheTracker(dirent)) {
779  // Serve retired inode from page cache tracker; even if we find it in the
780  // catalog, we replace the dirent by the page cache tracker version to
781  // not confuse open file handles
783  "cvmfs_getattr %" PRIu64 " "
784  "served from page cache tracker",
785  ino);
786  shash::Any hash;
787  struct stat info;
788  bool is_open = mount_point_->page_cache_tracker()->GetInfoIfOpen(ino, &hash,
789  &info);
790  if (is_open) {
792  if (found && HasDifferentContent(dirent, hash, info)) {
793  // We should from now on provide the new inode information instead
794  // of the stale one. To this end, we need to invalidate the dentry to
795  // trigger a fresh LOOKUP call
796  uint64_t parent_ino;
797  NameString name;
798  if (mount_point_->inode_tracker()->FindDentry(dirent.inode(),
799  &parent_ino, &name)) {
800  fuse_remounter_->InvalidateDentry(parent_ino, name);
801  }
803  }
804  fuse_reply_attr(req, &info, GetKcacheTimeout());
805  return;
806  }
807  }
809 
810  if (!found) {
811  ReplyNegative(dirent, req);
812  return;
813  }
814 
815  struct stat info = dirent.GetStatStructure();
816 
817  fuse_reply_attr(req, &info, GetKcacheTimeout());
818 }
819 
820 
824 static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino) {
826 
828  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
829  FuseInterruptCue ic(&req);
830  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
831 
833  ino = mount_point_->catalog_mgr()->MangleInode(ino);
834  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_readlink on inode: %" PRIu64,
835  uint64_t(ino));
836 
838  const bool found = GetDirentForInode(ino, &dirent);
839  TraceInode(Tracer::kEventReadlink, ino, "readlink()");
841 
842  if (!found) {
843  ReplyNegative(dirent, req);
844  return;
845  }
846 
847  if (!dirent.IsLink()) {
848  fuse_reply_err(req, EINVAL);
849  return;
850  }
851 
852  fuse_reply_readlink(req, dirent.symlink().c_str());
853 }
854 
855 
856 static void AddToDirListing(const fuse_req_t req, const char *name,
857  const struct stat *stat_info,
858  BigVector<char> *listing) {
859  LogCvmfs(kLogCvmfs, kLogDebug, "Add to listing: %s, inode %" PRIu64, name,
860  uint64_t(stat_info->st_ino));
861  size_t remaining_size = listing->capacity() - listing->size();
862  const size_t entry_size = fuse_add_direntry(req, NULL, 0, name, stat_info, 0);
863 
864  while (entry_size > remaining_size) {
865  listing->DoubleCapacity();
866  remaining_size = listing->capacity() - listing->size();
867  }
868 
869  char *buffer;
870  bool large_alloc;
871  listing->ShareBuffer(&buffer, &large_alloc);
872  fuse_add_direntry(req, buffer + listing->size(), remaining_size, name,
873  stat_info, listing->size() + entry_size);
874  listing->SetSize(listing->size() + entry_size);
875 }
876 
877 
881 static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino,
882  struct fuse_file_info *fi) {
884 
885  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
886  FuseInterruptCue ic(&req);
887  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
889 
892  ino = catalog_mgr->MangleInode(ino);
893  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64,
894  uint64_t(ino));
895  if (!CheckVoms(*fuse_ctx)) {
897  fuse_reply_err(req, EACCES);
898  return;
899  }
900 
901  TraceInode(Tracer::kEventOpenDir, ino, "opendir()");
902  PathString path;
904  bool found = GetPathForInode(ino, &path);
905  if (!found) {
907  fuse_reply_err(req, ENOENT);
908  return;
909  }
910  found = GetDirentForInode(ino, &d);
911 
912  if (!found) {
914  ReplyNegative(d, req);
915  return;
916  }
917  if (!d.IsDirectory()) {
919  fuse_reply_err(req, ENOTDIR);
920  return;
921  }
922 
923  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64 ", path %s",
924  uint64_t(ino), path.c_str());
925 
926  // Build listing
927  BigVector<char> fuse_listing(512);
928 
929  // Add current directory link
930  struct stat info;
931  info = d.GetStatStructure();
932  AddToDirListing(req, ".", &info, &fuse_listing);
933 
934  // Add parent directory link
936  if (d.inode() != catalog_mgr->GetRootInode()
937  && (GetDirentForPath(GetParentPath(path), &p) > 0)) {
938  info = p.GetStatStructure();
939  AddToDirListing(req, "..", &info, &fuse_listing);
940  }
941 
942  // Add all names
943  catalog::StatEntryList listing_from_catalog;
944  bool retval = catalog_mgr->ListingStat(path, &listing_from_catalog);
945 
946  if (!retval) {
948  fuse_listing.Clear(); // Buffer is shared, empty manually
949 
951  "EIO (03): failed to open directory at %s", path.c_str());
954  fuse_reply_err(req, EIO);
955  return;
956  }
957  for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
958  // Fix inodes
959  PathString entry_path;
960  entry_path.Assign(path);
961  entry_path.Append("/", 1);
962  entry_path.Append(listing_from_catalog.AtPtr(i)->name.GetChars(),
963  listing_from_catalog.AtPtr(i)->name.GetLength());
964 
965  catalog::DirectoryEntry entry_dirent;
966  if (!GetDirentForPath(entry_path, &entry_dirent)) {
967  LogCvmfs(kLogCvmfs, kLogDebug, "listing entry %s vanished, skipping",
968  entry_path.c_str());
969  continue;
970  }
971 
972  struct stat fixed_info = listing_from_catalog.AtPtr(i)->info;
973  fixed_info.st_ino = entry_dirent.inode();
974  AddToDirListing(req, listing_from_catalog.AtPtr(i)->name.c_str(),
975  &fixed_info, &fuse_listing);
976  }
978 
979  DirectoryListing stream_listing;
980  stream_listing.size = fuse_listing.size();
981  stream_listing.capacity = fuse_listing.capacity();
982  bool large_alloc;
983  fuse_listing.ShareBuffer(&stream_listing.buffer, &large_alloc);
984  if (large_alloc)
985  stream_listing.capacity = 0;
986 
987  // Save the directory listing and return a handle to the listing
988  {
991  "linking directory handle %lu to dir inode: %" PRIu64,
992  next_directory_handle_, uint64_t(ino));
993  (*directory_handles_)[next_directory_handle_] = stream_listing;
994  fi->fh = next_directory_handle_;
996  }
999 
1000 #if (FUSE_VERSION >= 30)
1001 #ifdef CVMFS_ENABLE_FUSE3_CACHE_READDIR
1002  // This affects only reads on the same open directory handle (e.g. multiple
1003  // reads with rewinddir() between them). A new opendir on the same directory
1004  // will trigger readdir calls independently of this setting.
1005  fi->cache_readdir = 1;
1006 #endif
1007 #endif
1008  fuse_reply_open(req, fi);
1009 }
1010 
1011 
1015 static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino,
1016  struct fuse_file_info *fi) {
1018 
1019  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1021  "cvmfs_releasedir on inode %" PRIu64 ", handle %lu", uint64_t(ino),
1022  fi->fh);
1023 
1024  int reply = 0;
1025 
1026  {
1028  DirectoryHandles::iterator iter_handle = directory_handles_->find(fi->fh);
1029  if (iter_handle != directory_handles_->end()) {
1030  if (iter_handle->second.capacity == 0)
1031  smunmap(iter_handle->second.buffer);
1032  else
1033  free(iter_handle->second.buffer);
1034  directory_handles_->erase(iter_handle);
1036  } else {
1037  reply = EINVAL;
1038  }
1039  }
1040 
1041  fuse_reply_err(req, reply);
1042 }
1043 
1044 
1048 static void ReplyBufferSlice(const fuse_req_t req, const char *buffer,
1049  const size_t buffer_size, const off_t offset,
1050  const size_t max_size) {
1051  if (offset < static_cast<int>(buffer_size)) {
1052  fuse_reply_buf(
1053  req, buffer + offset,
1054  std::min(static_cast<size_t>(buffer_size - offset), max_size));
1055  } else {
1056  fuse_reply_buf(req, NULL, 0);
1057  }
1058 }
1059 
1060 
1064 static void cvmfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
1065  off_t off, struct fuse_file_info *fi) {
1067 
1069  "cvmfs_readdir on inode %" PRIu64
1070  " reading %lu bytes from offset %ld",
1071  static_cast<uint64_t>(mount_point_->catalog_mgr()->MangleInode(ino)),
1072  size, off);
1073 
1074  DirectoryListing listing;
1075 
1077  DirectoryHandles::const_iterator iter_handle = directory_handles_->find(
1078  fi->fh);
1079  if (iter_handle != directory_handles_->end()) {
1080  listing = iter_handle->second;
1081 
1082  ReplyBufferSlice(req, listing.buffer, listing.size, off, size);
1083  return;
1084  }
1085 
1086  fuse_reply_err(req, EINVAL);
1087 }
1088 
1090  struct fuse_file_info *fi) {
1092  fi->keep_cache = od.keep_cache;
1093  fi->direct_io = od.direct_io;
1094  if (fi->direct_io)
1096 }
1097 
1098 
1099 #ifdef __APPLE__
1100 // On macOS, xattr on a symlink opens and closes the file (with O_SYMLINK)
1101 // around the actual getxattr call. In order to not run into an I/O error
1102 // we use a special file handle for symlinks, from which one cannot read.
1103 static const uint64_t kFileHandleIgnore = static_cast<uint64_t>(2) << 60;
1104 #endif
1105 
1112 static void cvmfs_open(fuse_req_t req, fuse_ino_t ino,
1113  struct fuse_file_info *fi) {
1115 
1116  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1117  FuseInterruptCue ic(&req);
1118  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1119  fuse_remounter_->fence()->Enter();
1121  ino = catalog_mgr->MangleInode(ino);
1122  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_open on inode: %" PRIu64,
1123  uint64_t(ino));
1124 
1125  int fd = -1;
1126  catalog::DirectoryEntry dirent;
1127  PathString path;
1128 
1129  bool found = GetPathForInode(ino, &path);
1130  if (!found) {
1131  fuse_remounter_->fence()->Leave();
1132  fuse_reply_err(req, ENOENT);
1133  return;
1134  }
1135  found = GetDirentForInode(ino, &dirent);
1136  if (!found) {
1137  fuse_remounter_->fence()->Leave();
1138  ReplyNegative(dirent, req);
1139  return;
1140  }
1141 
1142  if (!CheckVoms(*fuse_ctx)) {
1143  fuse_remounter_->fence()->Leave();
1144  fuse_reply_err(req, EACCES);
1145  return;
1146  }
1147 
1148  mount_point_->tracer()->Trace(Tracer::kEventOpen, path, "open()");
1149  // Don't check. Either done by the OS or one wants to purposefully work
1150  // around wrong open flags
1151  // if ((fi->flags & 3) != O_RDONLY) {
1152  // fuse_reply_err(req, EROFS);
1153  // return;
1154  // }
1155 #ifdef __APPLE__
1156  if ((fi->flags & O_SHLOCK) || (fi->flags & O_EXLOCK)) {
1157  fuse_remounter_->fence()->Leave();
1158  fuse_reply_err(req, EOPNOTSUPP);
1159  return;
1160  }
1161  if (fi->flags & O_SYMLINK) {
1162  fuse_remounter_->fence()->Leave();
1163  fi->fh = kFileHandleIgnore;
1164  fuse_reply_open(req, fi);
1165  return;
1166  }
1167 #endif
1168  if (fi->flags & O_EXCL) {
1169  fuse_remounter_->fence()->Leave();
1170  fuse_reply_err(req, EEXIST);
1171  return;
1172  }
1173 
1174  perf::Inc(file_system_->n_fs_open()); // Count actual open / fetch operations
1175 
1177  if (!dirent.IsChunkedFile()) {
1178  if (dirent.IsDirectIo()) {
1179  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1180  } else {
1181  open_directives = mount_point_->page_cache_tracker()->Open(
1182  ino, dirent.checksum(), dirent.GetStatStructure());
1183  }
1184  fuse_remounter_->fence()->Leave();
1185  } else {
1187  "chunked file %s opened (download delayed to read() call)",
1188  path.c_str());
1189 
1190  if (!IncAndCheckNoOpenFiles()) {
1192  fuse_remounter_->fence()->Leave();
1193  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1194  fuse_reply_err(req, EMFILE);
1196  return;
1197  }
1198 
1199  // Figure out unique inode from annotated catalog
1200  // TODO(jblomer): we only need to lookup if the inode is not from the
1201  // current generation
1202  catalog::DirectoryEntry dirent_origin;
1203  if (!catalog_mgr->LookupPath(path, catalog::kLookupDefault,
1204  &dirent_origin)) {
1205  fuse_remounter_->fence()->Leave();
1207  "chunked file %s vanished unexpectedly", path.c_str());
1208  fuse_reply_err(req, ENOENT);
1209  return;
1210  }
1211  const uint64_t unique_inode = dirent_origin.inode();
1212 
1213  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1214  chunk_tables->Lock();
1215  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1216  chunk_tables->Unlock();
1217 
1218  // Retrieve File chunks from the catalog
1220  if (!catalog_mgr->ListFileChunks(path, dirent.hash_algorithm(),
1221  chunks.weak_ref())
1222  || chunks->IsEmpty()) {
1223  fuse_remounter_->fence()->Leave();
1225  "EIO (04): failed to open file %s. "
1226  "It is marked as 'chunked', but no chunks found.",
1227  path.c_str());
1230  fuse_reply_err(req, EIO);
1231  return;
1232  }
1233  fuse_remounter_->fence()->Leave();
1234 
1235  chunk_tables->Lock();
1236  // Check again to avoid race
1237  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1238  chunk_tables->inode2chunks.Insert(
1239  unique_inode, FileChunkReflist(chunks.Release(), path,
1240  dirent.compression_algorithm(),
1241  dirent.IsExternalFile()));
1242  chunk_tables->inode2references.Insert(unique_inode, 1);
1243  } else {
1244  uint32_t refctr;
1245  bool retval = chunk_tables->inode2references.Lookup(unique_inode,
1246  &refctr);
1247  assert(retval);
1248  chunk_tables->inode2references.Insert(unique_inode, refctr + 1);
1249  }
1250  } else {
1251  fuse_remounter_->fence()->Leave();
1252  uint32_t refctr;
1253  bool retval = chunk_tables->inode2references.Lookup(unique_inode,
1254  &refctr);
1255  assert(retval);
1256  chunk_tables->inode2references.Insert(unique_inode, refctr + 1);
1257  }
1258 
1259  // Update the chunk handle list
1261  "linking chunk handle %lu to unique inode: %" PRIu64,
1262  chunk_tables->next_handle, uint64_t(unique_inode));
1263  chunk_tables->handle2fd.Insert(chunk_tables->next_handle, ChunkFd());
1264  chunk_tables->handle2uniqino.Insert(chunk_tables->next_handle,
1265  unique_inode);
1266 
1267  // Generate artificial content hash as hash over chunk hashes
1268  // TODO(jblomer): we may want to cache the result in the chunk tables
1269  FileChunkReflist chunk_reflist;
1270  bool retval = chunk_tables->inode2chunks.Lookup(unique_inode,
1271  &chunk_reflist);
1272  assert(retval);
1273 
1274  fi->fh = chunk_tables->next_handle;
1275  if (dirent.IsDirectIo()) {
1276  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1277  } else {
1278  open_directives = mount_point_->page_cache_tracker()->Open(
1279  ino, chunk_reflist.HashChunkList(), dirent.GetStatStructure());
1280  }
1281  FillOpenFlags(open_directives, fi);
1282  fi->fh = static_cast<uint64_t>(-static_cast<int64_t>(fi->fh));
1283  ++chunk_tables->next_handle;
1284  chunk_tables->Unlock();
1285 
1286  fuse_reply_open(req, fi);
1287  return;
1288  }
1289 
1290  Fetcher *this_fetcher = dirent.IsExternalFile()
1292  : mount_point_->fetcher();
1293  CacheManager::Label label;
1294  label.path = path.ToString();
1295  label.size = dirent.size();
1296  label.zip_algorithm = dirent.compression_algorithm();
1299  if (dirent.IsExternalFile())
1301  fd = this_fetcher->Fetch(
1302  CacheManager::LabeledObject(dirent.checksum(), label));
1303 
1304  if (fd >= 0) {
1305  if (IncAndCheckNoOpenFiles()) {
1306  LogCvmfs(kLogCvmfs, kLogDebug, "file %s opened (fd %d)", path.c_str(),
1307  fd);
1308  fi->fh = fd;
1309  FillOpenFlags(open_directives, fi);
1310  fuse_reply_open(req, fi);
1311  return;
1312  } else {
1313  if (file_system_->cache_mgr()->Close(fd) == 0)
1315  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1316  // not returning an fd, so close the page cache tracker entry if required
1317  if (!dirent.IsDirectIo()) {
1318  fuse_remounter_->fence()->Enter();
1320  fuse_remounter_->fence()->Leave();
1321  }
1322  fuse_reply_err(req, EMFILE);
1324  return;
1325  }
1326  assert(false);
1327  }
1328 
1329  // fd < 0
1330  // the download has failed. Close the page cache tracker entry if required
1331  if (!dirent.IsDirectIo()) {
1332  fuse_remounter_->fence()->Enter();
1334  fuse_remounter_->fence()->Leave();
1335  }
1336 
1338  "failed to open inode: %" PRIu64 ", CAS key %s, error code %d",
1339  uint64_t(ino), dirent.checksum().ToString().c_str(), errno);
1340  if (errno == EMFILE) {
1341  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1342  fuse_reply_err(req, EMFILE);
1344  return;
1345  }
1346 
1348 
1350  if (EIO == errno || EIO == -fd) {
1352  "EIO (06): Failed to open file %s", path.c_str());
1355  }
1356 
1357  fuse_reply_err(req, -fd);
1358 }
1359 
1360 
1364 static void cvmfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1365  struct fuse_file_info *fi) {
1367 
1369  "cvmfs_read inode: %" PRIu64 " reading %lu bytes from offset %ld "
1370  "fd %lu",
1371  uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)), size, off,
1372  fi->fh);
1374 
1375 #ifdef __APPLE__
1376  if (fi->fh == kFileHandleIgnore) {
1377  fuse_reply_err(req, EBADF);
1378  return;
1379  }
1380 #endif
1381 
1382  // Get data chunk (<=128k guaranteed by Fuse)
1383  char *data = static_cast<char *>(alloca(size));
1384  unsigned int overall_bytes_fetched = 0;
1385 
1386  int64_t fd = static_cast<int64_t>(fi->fh);
1387  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1389 
1390  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1391  FuseInterruptCue ic(&req);
1392  const ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid,
1393  &ic);
1394 
1395  // Do we have a a chunked file?
1396  if (fd < 0) {
1397  const uint64_t chunk_handle = abs_fd;
1398  uint64_t unique_inode;
1399  ChunkFd chunk_fd;
1400  FileChunkReflist chunks;
1401  bool retval;
1402 
1403  // Fetch unique inode, chunk list and file descriptor
1404  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1405  chunk_tables->Lock();
1406  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1407  if (!retval) {
1408  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1409  unique_inode = ino;
1410  }
1411  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &chunks);
1412  assert(retval);
1413  chunk_tables->Unlock();
1414 
1415  unsigned chunk_idx = chunks.FindChunkIdx(off);
1416 
1417  // Lock chunk handle
1418  pthread_mutex_t *handle_lock = chunk_tables->Handle2Lock(chunk_handle);
1419  MutexLockGuard m(handle_lock);
1420  chunk_tables->Lock();
1421  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1422  assert(retval);
1423  chunk_tables->Unlock();
1424 
1425  // Fetch all needed chunks and read the requested data
1426  off_t offset_in_chunk = off - chunks.list->AtPtr(chunk_idx)->offset();
1427  do {
1428  // Open file descriptor to chunk
1429  if ((chunk_fd.fd == -1) || (chunk_fd.chunk_idx != chunk_idx)) {
1430  if (chunk_fd.fd != -1)
1431  file_system_->cache_mgr()->Close(chunk_fd.fd);
1432  Fetcher *this_fetcher = chunks.external_data
1434  : mount_point_->fetcher();
1435  CacheManager::Label label;
1436  label.path = chunks.path.ToString();
1437  label.size = chunks.list->AtPtr(chunk_idx)->size();
1438  label.zip_algorithm = chunks.compression_alg;
1442  if (chunks.external_data) {
1444  label.range_offset = chunks.list->AtPtr(chunk_idx)->offset();
1445  }
1446  chunk_fd.fd = this_fetcher->Fetch(CacheManager::LabeledObject(
1447  chunks.list->AtPtr(chunk_idx)->content_hash(), label));
1448  if (chunk_fd.fd < 0) {
1449  chunk_fd.fd = -1;
1450  chunk_tables->Lock();
1451  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1452  chunk_tables->Unlock();
1453 
1455  "EIO (05): Failed to fetch chunk %d from file %s", chunk_idx,
1456  chunks.path.ToString().c_str());
1459  fuse_reply_err(req, EIO);
1460  return;
1461  }
1462  chunk_fd.chunk_idx = chunk_idx;
1463  }
1464 
1465  LogCvmfs(kLogCvmfs, kLogDebug, "reading from chunk fd %d", chunk_fd.fd);
1466  // Read data from chunk
1467  const size_t bytes_to_read = size - overall_bytes_fetched;
1468  const size_t remaining_bytes_in_chunk = chunks.list->AtPtr(chunk_idx)
1469  ->size()
1470  - offset_in_chunk;
1471  size_t bytes_to_read_in_chunk = std::min(bytes_to_read,
1472  remaining_bytes_in_chunk);
1473  const int64_t bytes_fetched = file_system_->cache_mgr()->Pread(
1474  chunk_fd.fd,
1475  data + overall_bytes_fetched,
1476  bytes_to_read_in_chunk,
1477  offset_in_chunk);
1478 
1479  if (bytes_fetched < 0) {
1480  LogCvmfs(kLogCvmfs, kLogSyslogErr, "read err no %" PRId64 " (%s)",
1481  bytes_fetched, chunks.path.ToString().c_str());
1482  chunk_tables->Lock();
1483  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1484  chunk_tables->Unlock();
1485  if (EIO == errno || EIO == -bytes_fetched) {
1487  "EIO (07): Failed to read chunk %d from file %s", chunk_idx,
1488  chunks.path.ToString().c_str());
1491  }
1492  fuse_reply_err(req, -bytes_fetched);
1493  return;
1494  }
1495  overall_bytes_fetched += bytes_fetched;
1496 
1497  // Proceed to the next chunk to keep on reading data
1498  ++chunk_idx;
1499  offset_in_chunk = 0;
1500  } while ((overall_bytes_fetched < size)
1501  && (chunk_idx < chunks.list->size()));
1502 
1503  // Update chunk file descriptor
1504  chunk_tables->Lock();
1505  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1506  chunk_tables->Unlock();
1507  LogCvmfs(kLogCvmfs, kLogDebug, "released chunk file descriptor %d",
1508  chunk_fd.fd);
1509  } else {
1510  int64_t nbytes = file_system_->cache_mgr()->Pread(abs_fd, data, size, off);
1511  if (nbytes < 0) {
1512  if (EIO == errno || EIO == -nbytes) {
1513  PathString path;
1514  bool found = GetPathForInode(ino, &path);
1515  if (found) {
1517  "EIO (08): Failed to read file %s", path.ToString().c_str());
1518  } else {
1520  "EIO (08): Failed to read from %s - <unknown inode>",
1521  path.ToString().c_str());
1522  }
1525  }
1526  fuse_reply_err(req, -nbytes);
1527  return;
1528  }
1529  overall_bytes_fetched = nbytes;
1530  }
1531 
1532  // Push it to user
1533  fuse_reply_buf(req, data, overall_bytes_fetched);
1534  LogCvmfs(kLogCvmfs, kLogDebug, "pushed %d bytes to user",
1535  overall_bytes_fetched);
1536 }
1537 
1538 
1542 static void cvmfs_release(fuse_req_t req, fuse_ino_t ino,
1543  struct fuse_file_info *fi) {
1545 
1546  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1547  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_release on inode: %" PRIu64,
1548  uint64_t(ino));
1549 
1550 #ifdef __APPLE__
1551  if (fi->fh == kFileHandleIgnore) {
1552  fuse_reply_err(req, 0);
1553  return;
1554  }
1555 #endif
1556 
1557  int64_t fd = static_cast<int64_t>(fi->fh);
1558  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1561  }
1563 
1564  // do we have a chunked file?
1565  if (fd < 0) {
1566  const uint64_t chunk_handle = abs_fd;
1567  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk handle %" PRIu64,
1568  chunk_handle);
1569  uint64_t unique_inode;
1570  ChunkFd chunk_fd;
1571  FileChunkReflist chunks;
1572  uint32_t refctr;
1573  bool retval;
1574 
1575  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1576  chunk_tables->Lock();
1577  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1578  if (!retval) {
1579  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1580  unique_inode = ino;
1581  } else {
1582  chunk_tables->handle2uniqino.Erase(chunk_handle);
1583  }
1584  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1585  assert(retval);
1586  chunk_tables->handle2fd.Erase(chunk_handle);
1587 
1588  retval = chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1589  assert(retval);
1590  refctr--;
1591  if (refctr == 0) {
1592  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk list for inode %" PRIu64,
1593  uint64_t(unique_inode));
1594  FileChunkReflist to_delete;
1595  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &to_delete);
1596  assert(retval);
1597  chunk_tables->inode2references.Erase(unique_inode);
1598  chunk_tables->inode2chunks.Erase(unique_inode);
1599  delete to_delete.list;
1600  } else {
1601  chunk_tables->inode2references.Insert(unique_inode, refctr);
1602  }
1603  chunk_tables->Unlock();
1604 
1605  if (chunk_fd.fd != -1)
1606  file_system_->cache_mgr()->Close(chunk_fd.fd);
1608  } else {
1609  if (file_system_->cache_mgr()->Close(abs_fd) == 0) {
1611  }
1612  }
1613  fuse_reply_err(req, 0);
1614 }
1615 
1623 static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino) {
1624  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1625  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_statfs on inode: %" PRIu64,
1626  uint64_t(ino));
1627 
1628  TraceInode(Tracer::kEventStatFs, ino, "statfs()");
1629 
1631 
1632  // Unmanaged cache (no lock needed - statfs is never modified)
1635  LogCvmfs(kLogCvmfs, kLogDebug, "QuotaManager does not support statfs");
1636  fuse_reply_statfs(req, (mount_point_->statfs_cache()->info()));
1637  return;
1638  }
1639 
1641 
1642  const uint64_t deadline = *mount_point_->statfs_cache()->expiry_deadline();
1643  struct statvfs *info = mount_point_->statfs_cache()->info();
1644 
1645  // cached version still valid
1646  if (platform_monotonic_time() < deadline) {
1648  fuse_reply_statfs(req, info);
1649  return;
1650  }
1651 
1652  uint64_t available = 0;
1653  uint64_t size = file_system_->cache_mgr()->quota_mgr()->GetSize();
1654  uint64_t capacity = file_system_->cache_mgr()->quota_mgr()->GetCapacity();
1655  // Fuse/OS X doesn't like values < 512
1656  info->f_bsize = info->f_frsize = 512;
1657 
1658  if (capacity == (uint64_t)(-1)) {
1659  // Unknown capacity, set capacity = size
1660  info->f_blocks = size / info->f_bsize;
1661  } else {
1662  // Take values from LRU module
1663  info->f_blocks = capacity / info->f_bsize;
1664  available = capacity - size;
1665  }
1666 
1667  info->f_bfree = info->f_bavail = available / info->f_bsize;
1668 
1669  // Inodes / entries
1670  fuse_remounter_->fence()->Enter();
1671  uint64_t all_inodes = mount_point_->catalog_mgr()->all_inodes();
1672  uint64_t loaded_inode = mount_point_->catalog_mgr()->loaded_inodes();
1673  info->f_files = all_inodes;
1674  info->f_ffree = info->f_favail = all_inodes - loaded_inode;
1675  fuse_remounter_->fence()->Leave();
1676 
1680 
1681  fuse_reply_statfs(req, info);
1682 }
1683 
1684 #ifdef __APPLE__
1685 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1686  size_t size, uint32_t position)
1687 #else
1688 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1689  size_t size)
1690 #endif
1691 {
1692  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1693  FuseInterruptCue ic(&req);
1694  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1695 
1696  fuse_remounter_->fence()->Enter();
1698  ino = catalog_mgr->MangleInode(ino);
1700  "cvmfs_getxattr on inode: %" PRIu64 " for xattr: %s", uint64_t(ino),
1701  name);
1702  if (!CheckVoms(*fuse_ctx)) {
1703  fuse_remounter_->fence()->Leave();
1704  fuse_reply_err(req, EACCES);
1705  return;
1706  }
1707  TraceInode(Tracer::kEventGetXAttr, ino, "getxattr()");
1708 
1709  vector<string> tokens_mode_machine = SplitString(name, '~');
1710  vector<string> tokens_mode_human = SplitString(name, '@');
1711 
1712  int32_t attr_req_page = 0;
1713  MagicXattrMode xattr_mode = kXattrMachineMode;
1714  string attr;
1715 
1716  bool attr_req_is_valid = false;
1717  const sanitizer::PositiveIntegerSanitizer page_num_sanitizer;
1718 
1719  if (tokens_mode_human.size() > 1) {
1720  const std::string token = tokens_mode_human[tokens_mode_human.size() - 1];
1721  if (token == "?") {
1722  attr_req_is_valid = true;
1723  attr_req_page = -1;
1724  } else {
1725  if (page_num_sanitizer.IsValid(token)) {
1726  attr_req_is_valid = true;
1727  attr_req_page = static_cast<int32_t>(String2Uint64(token));
1728  }
1729  }
1730  xattr_mode = kXattrHumanMode;
1731  attr = tokens_mode_human[0];
1732  } else if (tokens_mode_machine.size() > 1) {
1733  const std::string
1734  token = tokens_mode_machine[tokens_mode_machine.size() - 1];
1735  if (token == "?") {
1736  attr_req_is_valid = true;
1737  attr_req_page = -1;
1738  } else {
1739  if (page_num_sanitizer.IsValid(token)) {
1740  attr_req_is_valid = true;
1741  attr_req_page = static_cast<int32_t>(String2Uint64(token));
1742  }
1743  }
1744  xattr_mode = kXattrMachineMode;
1745  attr = tokens_mode_machine[0];
1746 
1747  } else {
1748  attr_req_is_valid = true;
1749  attr = tokens_mode_machine[0];
1750  }
1751 
1752  if (!attr_req_is_valid) {
1753  fuse_remounter_->fence()->Leave();
1754  fuse_reply_err(req, ENODATA);
1755  return;
1756  }
1757 
1759  const bool found = GetDirentForInode(ino, &d);
1760 
1761  if (!found) {
1762  fuse_remounter_->fence()->Leave();
1763  ReplyNegative(d, req);
1764  return;
1765  }
1766 
1767  bool retval;
1768  XattrList xattrs;
1769  PathString path;
1770  retval = GetPathForInode(ino, &path);
1771 
1773  "cvmfs_statfs: Race condition? "
1774  "GetPathForInode did not succeed for path %s "
1775  "(path might have not been set)",
1776  path.c_str())) {
1777  fuse_remounter_->fence()->Leave();
1778  fuse_reply_err(req, ESTALE);
1779  return;
1780  }
1781 
1782  if (d.IsLink()) {
1783  catalog::LookupOptions lookup_options = static_cast<catalog::LookupOptions>(
1785  catalog::DirectoryEntry raw_symlink;
1786  retval = catalog_mgr->LookupPath(path, lookup_options, &raw_symlink);
1787 
1789  "cvmfs_statfs: Race condition? "
1790  "LookupPath did not succeed for path %s",
1791  path.c_str())) {
1792  fuse_remounter_->fence()->Leave();
1793  fuse_reply_err(req, ESTALE);
1794  return;
1795  }
1796 
1797  d.set_symlink(raw_symlink.symlink());
1798  }
1799  if (d.HasXattrs()) {
1800  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1801 
1803  "cvmfs_statfs: Race condition? "
1804  "LookupXattrs did not succeed for path %s",
1805  path.c_str())) {
1806  fuse_remounter_->fence()->Leave();
1807  fuse_reply_err(req, ESTALE);
1808  return;
1809  }
1810  }
1811 
1812  bool magic_xattr_success = true;
1813  MagicXattrRAIIWrapper magic_xattr(
1814  mount_point_->magic_xattr_mgr()->GetLocked(attr, path, &d));
1815  if (!magic_xattr.IsNull()) {
1816  magic_xattr_success = magic_xattr->PrepareValueFencedProtected(
1817  fuse_ctx->gid);
1818  }
1819 
1820  fuse_remounter_->fence()->Leave();
1821 
1822  if (!magic_xattr_success) {
1823  fuse_reply_err(req, ENOATTR);
1824  return;
1825  }
1826 
1827  std::pair<bool, std::string> attribute_result;
1828 
1829  if (!magic_xattr.IsNull()) {
1830  attribute_result = magic_xattr->GetValue(attr_req_page, xattr_mode);
1831  } else {
1832  if (!xattrs.Get(attr, &attribute_result.second)) {
1833  fuse_reply_err(req, ENOATTR);
1834  return;
1835  }
1836  attribute_result.first = true;
1837  }
1838 
1839  if (!attribute_result.first) {
1840  fuse_reply_err(req, ENODATA);
1841  } else if (size == 0) {
1842  fuse_reply_xattr(req, attribute_result.second.length());
1843  } else if (size >= attribute_result.second.length()) {
1844  fuse_reply_buf(req, &attribute_result.second[0],
1845  attribute_result.second.length());
1846  } else {
1847  fuse_reply_err(req, ERANGE);
1848  }
1849 }
1850 
1851 
1852 static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
1853  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1854  FuseInterruptCue ic(&req);
1855  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1856 
1857  fuse_remounter_->fence()->Enter();
1859  ino = catalog_mgr->MangleInode(ino);
1860  TraceInode(Tracer::kEventListAttr, ino, "listxattr()");
1862  "cvmfs_listxattr on inode: %" PRIu64 ", size %zu [visibility %d]",
1863  uint64_t(ino), size, mount_point_->magic_xattr_mgr()->visibility());
1864 
1866  const bool found = GetDirentForInode(ino, &d);
1867  XattrList xattrs;
1868  if (d.HasXattrs()) {
1869  PathString path;
1870  bool retval = GetPathForInode(ino, &path);
1871 
1873  "cvmfs_listxattr: Race condition? "
1874  "GetPathForInode did not succeed for ino %lu",
1875  ino)) {
1876  fuse_remounter_->fence()->Leave();
1877  fuse_reply_err(req, ESTALE);
1878  return;
1879  }
1880 
1881  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1883  "cvmfs_listxattr: Race condition? "
1884  "LookupXattrs did not succeed for ino %lu",
1885  ino)) {
1886  fuse_remounter_->fence()->Leave();
1887  fuse_reply_err(req, ESTALE);
1888  return;
1889  }
1890  }
1891  fuse_remounter_->fence()->Leave();
1892 
1893  if (!found) {
1894  ReplyNegative(d, req);
1895  return;
1896  }
1897 
1898  string attribute_list;
1899  attribute_list = mount_point_->magic_xattr_mgr()->GetListString(&d);
1900  attribute_list += xattrs.ListKeysPosix(attribute_list);
1901 
1902  if (size == 0) {
1903  fuse_reply_xattr(req, attribute_list.length());
1904  } else if (size >= attribute_list.length()) {
1905  if (attribute_list.empty())
1906  fuse_reply_buf(req, NULL, 0);
1907  else
1908  fuse_reply_buf(req, &attribute_list[0], attribute_list.length());
1909  } else {
1910  fuse_reply_err(req, ERANGE);
1911  }
1912 }
1913 
1914 bool Evict(const string &path) {
1915  catalog::DirectoryEntry dirent;
1916  fuse_remounter_->fence()->Enter();
1917  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1918 
1919  if (!found || !dirent.IsRegular()) {
1920  fuse_remounter_->fence()->Leave();
1921  return false;
1922  }
1923 
1924  if (!dirent.IsChunkedFile()) {
1925  fuse_remounter_->fence()->Leave();
1926  } else {
1927  FileChunkList chunks;
1929  PathString(path), dirent.hash_algorithm(), &chunks);
1930  fuse_remounter_->fence()->Leave();
1931  for (unsigned i = 0; i < chunks.size(); ++i) {
1933  chunks.AtPtr(i)->content_hash());
1934  }
1935  }
1936  file_system_->cache_mgr()->quota_mgr()->Remove(dirent.checksum());
1937  return true;
1938 }
1939 
1940 
1941 bool Pin(const string &path) {
1942  catalog::DirectoryEntry dirent;
1943  fuse_remounter_->fence()->Enter();
1944  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1945  if (!found || !dirent.IsRegular()) {
1946  fuse_remounter_->fence()->Leave();
1947  return false;
1948  }
1949 
1950  Fetcher *this_fetcher = dirent.IsExternalFile()
1952  : mount_point_->fetcher();
1953 
1954  if (!dirent.IsChunkedFile()) {
1955  fuse_remounter_->fence()->Leave();
1956  } else {
1957  FileChunkList chunks;
1959  PathString(path), dirent.hash_algorithm(), &chunks);
1960  fuse_remounter_->fence()->Leave();
1961  for (unsigned i = 0; i < chunks.size(); ++i) {
1962  bool retval = file_system_->cache_mgr()->quota_mgr()->Pin(
1963  chunks.AtPtr(i)->content_hash(),
1964  chunks.AtPtr(i)->size(),
1965  "Part of " + path,
1966  false);
1967  if (!retval)
1968  return false;
1969  int fd = -1;
1970  CacheManager::Label label;
1971  label.path = path;
1972  label.size = chunks.AtPtr(i)->size();
1973  label.zip_algorithm = dirent.compression_algorithm();
1976  if (dirent.IsExternalFile()) {
1978  label.range_offset = chunks.AtPtr(i)->offset();
1979  }
1980  fd = this_fetcher->Fetch(
1981  CacheManager::LabeledObject(chunks.AtPtr(i)->content_hash(), label));
1982  if (fd < 0) {
1983  return false;
1984  }
1985  file_system_->cache_mgr()->Close(fd);
1986  }
1987  return true;
1988  }
1989 
1990  bool retval = file_system_->cache_mgr()->quota_mgr()->Pin(
1991  dirent.checksum(), dirent.size(), path, false);
1992  if (!retval)
1993  return false;
1994  CacheManager::Label label;
1996  label.size = dirent.size();
1997  label.path = path;
1998  label.zip_algorithm = dirent.compression_algorithm();
1999  int fd = this_fetcher->Fetch(
2000  CacheManager::LabeledObject(dirent.checksum(), label));
2001  if (fd < 0) {
2002  return false;
2003  }
2004  file_system_->cache_mgr()->Close(fd);
2005  return true;
2006 }
2007 
2008 
2012 static void cvmfs_init(void *userdata, struct fuse_conn_info *conn) {
2013  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_init");
2014 
2015  // NFS support
2016 #ifdef CVMFS_NFS_SUPPORT
2017  conn->want |= FUSE_CAP_EXPORT_SUPPORT;
2018 #endif
2019 
2020  if (mount_point_->enforce_acls()) {
2021 #ifdef FUSE_CAP_POSIX_ACL
2022  if ((conn->capable & FUSE_CAP_POSIX_ACL) == 0) {
2024  "FUSE: ACL support requested but missing fuse kernel support, "
2025  "aborting");
2026  }
2027  conn->want |= FUSE_CAP_POSIX_ACL;
2028  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, "enforcing ACLs");
2029 #else
2031  "FUSE: ACL support requested but not available in this version of "
2032  "libfuse %d, aborting",
2033  FUSE_VERSION);
2034 #endif
2035  }
2036 
2037  if (mount_point_->cache_symlinks()) {
2038 #ifdef FUSE_CAP_CACHE_SYMLINKS
2039  if ((conn->capable & FUSE_CAP_CACHE_SYMLINKS) == FUSE_CAP_CACHE_SYMLINKS) {
2040  conn->want |= FUSE_CAP_CACHE_SYMLINKS;
2041  LogCvmfs(kLogCvmfs, kLogDebug, "FUSE: Enable symlink caching");
2042 #ifndef FUSE_CAP_EXPIRE_ONLY
2043  LogCvmfs(
2045  "FUSE: Symlink caching enabled but no support for fuse_expire_entry. "
2046  "Symlinks will be cached but mountpoints on top of symlinks will "
2047  "break! "
2048  "Current libfuse %d is too old; required: libfuse >= 3.16, "
2049  "kernel >= 6.2-rc1",
2050  FUSE_VERSION);
2051 #endif
2052  } else {
2054  LogCvmfs(
2056  "FUSE: Symlink caching requested but missing fuse kernel support, "
2057  "falling back to no caching");
2058  }
2059 #else
2062  "FUSE: Symlink caching requested but missing libfuse support, "
2063  "falling back to no caching. Current libfuse %d",
2064  FUSE_VERSION);
2065 #endif
2066  }
2067 
2068 #ifdef FUSE_CAP_EXPIRE_ONLY
2069  if ((conn->capable & FUSE_CAP_EXPIRE_ONLY) == FUSE_CAP_EXPIRE_ONLY
2070  && FUSE_VERSION >= FUSE_MAKE_VERSION(3, 16)) {
2072  LogCvmfs(kLogCvmfs, kLogDebug, "FUSE: Enable fuse_expire_entry ");
2073  } else if (mount_point_->cache_symlinks()) {
2074  LogCvmfs(
2076  "FUSE: Symlink caching enabled but no support for fuse_expire_entry. "
2077  "Symlinks will be cached but mountpoints on top of symlinks will "
2078  "break! "
2079  "Current libfuse %d; required: libfuse >= 3.16, kernel >= 6.2-rc1",
2080  FUSE_VERSION);
2081  }
2082 #endif
2083 }
2084 
2085 static void cvmfs_destroy(void *unused __attribute__((unused))) {
2086  // The debug log is already closed at this point
2087  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_destroy");
2088 }
2089 
2093 static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations) {
2094  memset(cvmfs_operations, 0, sizeof(*cvmfs_operations));
2095 
2096  // Init/Fini
2097  cvmfs_operations->init = cvmfs_init;
2098  cvmfs_operations->destroy = cvmfs_destroy;
2099 
2100  cvmfs_operations->lookup = cvmfs_lookup;
2101  cvmfs_operations->getattr = cvmfs_getattr;
2102  cvmfs_operations->readlink = cvmfs_readlink;
2103  cvmfs_operations->open = cvmfs_open;
2104  cvmfs_operations->read = cvmfs_read;
2105  cvmfs_operations->release = cvmfs_release;
2106  cvmfs_operations->opendir = cvmfs_opendir;
2107  cvmfs_operations->readdir = cvmfs_readdir;
2108  cvmfs_operations->releasedir = cvmfs_releasedir;
2109  cvmfs_operations->statfs = cvmfs_statfs;
2110  cvmfs_operations->getxattr = cvmfs_getxattr;
2111  cvmfs_operations->listxattr = cvmfs_listxattr;
2112  cvmfs_operations->forget = cvmfs_forget;
2113 #if (FUSE_VERSION >= 29)
2114  cvmfs_operations->forget_multi = cvmfs_forget_multi;
2115 #endif
2116 }
2117 
2118 // Called by cvmfs_talk when switching into read-only cache mode
2120  if (cvmfs::unpin_listener_) {
2122  cvmfs::unpin_listener_ = NULL;
2123  }
2127  }
2128 }
2129 
2130 bool SendFuseFd(const std::string &socket_path) {
2131  int fuse_fd;
2132 #if (FUSE_VERSION >= 30)
2133  fuse_fd = fuse_session_fd(*reinterpret_cast<struct fuse_session **>(
2135 #else
2136  fuse_fd = fuse_chan_fd(*reinterpret_cast<struct fuse_chan **>(
2138 #endif
2139  assert(fuse_fd >= 0);
2140  int sock_fd = ConnectSocket(socket_path);
2141  if (sock_fd < 0) {
2142  LogCvmfs(kLogCvmfs, kLogDebug, "cannot connect to socket %s: %d",
2143  socket_path.c_str(), errno);
2144  return false;
2145  }
2146  bool retval = SendFd2Socket(sock_fd, fuse_fd);
2147  close(sock_fd);
2148  return retval;
2149 }
2150 
2151 } // namespace cvmfs
2152 
2153 
2154 string *g_boot_error = NULL;
2155 
2156 __attribute__((
2157  visibility("default"))) loader::CvmfsExports *g_cvmfs_exports = NULL;
2158 
2165 
2166  virtual bool PrepareValueFenced() {
2167  catalogs_valid_until_ = cvmfs::fuse_remounter_->catalogs_valid_until();
2168  return true;
2169  }
2170 
2171  virtual void FinalizeValue() {
2172  if (catalogs_valid_until_ == MountPoint::kIndefiniteDeadline) {
2173  result_pages_.push_back("never (fixed root catalog)");
2174  return;
2175  } else {
2176  time_t now = time(NULL);
2177  result_pages_.push_back(StringifyInt((catalogs_valid_until_ - now) / 60));
2178  }
2179  }
2180 };
2181 
2183  virtual void FinalizeValue() {
2184  result_pages_.push_back(StringifyInt(
2185  cvmfs::inode_generation_info_.inode_generation
2186  + xattr_mgr_->mount_point()->catalog_mgr()->inode_gauge()));
2187  }
2188 };
2189 
2191  virtual void FinalizeValue() {
2192  result_pages_.push_back(
2194  }
2195 };
2196 
2198  virtual void FinalizeValue() {
2199  result_pages_.push_back(StringifyInt(cvmfs::pid_));
2200  }
2201 };
2202 
2204  virtual void FinalizeValue() {
2205  time_t now = time(NULL);
2206  uint64_t uptime = now - cvmfs::loader_exports_->boot_time;
2207  result_pages_.push_back(StringifyUint(uptime / 60));
2208  }
2209 };
2210 
2215 static void RegisterMagicXattrs() {
2217  mgr->Register("user.expires", new ExpiresMagicXattr());
2218  mgr->Register("user.inode_max", new InodeMaxMagicXattr());
2219  mgr->Register("user.pid", new PidMagicXattr());
2220  mgr->Register("user.maxfd", new MaxFdMagicXattr());
2221  mgr->Register("user.uptime", new UptimeMagicXattr());
2222 
2223  mgr->Freeze();
2224 }
2225 
2230 static FileSystem *InitSystemFs(const string &mount_path,
2231  const string &fqrn,
2232  FileSystem::FileSystemInfo fs_info) {
2233  fs_info.wait_workspace = false;
2234  FileSystem *file_system = FileSystem::Create(fs_info);
2235 
2236  if (file_system->boot_status() == loader::kFailLockWorkspace) {
2237  string fqrn_from_xattr;
2238  int retval = platform_getxattr(mount_path, "user.fqrn", &fqrn_from_xattr);
2239  if (!retval) {
2240  // Cvmfs not mounted anymore, but another cvmfs process is still in
2241  // shutdown procedure. Try again and wait for lock
2242  delete file_system;
2243  fs_info.wait_workspace = true;
2244  file_system = FileSystem::Create(fs_info);
2245  } else {
2246  if (fqrn_from_xattr == fqrn) {
2248  "repository already mounted on %s", mount_path.c_str());
2250  } else {
2252  "CernVM-FS repository %s already mounted on %s", fqrn.c_str(),
2253  mount_path.c_str());
2255  }
2256  }
2257  }
2258 
2259  return file_system;
2260 }
2261 
2262 
2263 static void InitOptionsMgr(const loader::LoaderExports *loader_exports) {
2264  if (loader_exports->version >= 3 && loader_exports->simple_options_parsing) {
2266  new DefaultOptionsTemplateManager(loader_exports->repository_name));
2267  } else {
2269  new DefaultOptionsTemplateManager(loader_exports->repository_name));
2270  }
2271 
2272  if (loader_exports->config_files != "") {
2273  vector<string> tokens = SplitString(loader_exports->config_files, ':');
2274  for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
2275  cvmfs::options_mgr_->ParsePath(tokens[i], false);
2276  }
2277  } else {
2279  }
2280 }
2281 
2282 
2283 static unsigned CheckMaxOpenFiles() {
2284  static unsigned max_open_files;
2285  static bool already_done = false;
2286 
2287  // check number of open files (lazy evaluation)
2288  if (!already_done) {
2289  unsigned soft_limit = 0;
2290  unsigned hard_limit = 0;
2291  GetLimitNoFile(&soft_limit, &hard_limit);
2292 
2293  if (soft_limit < cvmfs::kMinOpenFiles) {
2295  "Warning: current limits for number of open files are "
2296  "(%u/%u)\n"
2297  "CernVM-FS is likely to run out of file descriptors, "
2298  "set ulimit -n to at least %u",
2299  soft_limit, hard_limit, cvmfs::kMinOpenFiles);
2300  }
2301  max_open_files = soft_limit;
2302  already_done = true;
2303  }
2304 
2305  return max_open_files;
2306 }
2307 
2308 
2309 static int Init(const loader::LoaderExports *loader_exports) {
2310  g_boot_error = new string("unknown error");
2311  cvmfs::loader_exports_ = loader_exports;
2312 
2314 
2315  InitOptionsMgr(loader_exports);
2316 
2317  // We need logging set up before forking the watchdog
2319  loader_exports->repository_name);
2320 
2321  // Monitor, check for maximum number of open files
2322  if (cvmfs::UseWatchdog()) {
2323  auto_umount::SetMountpoint(loader_exports->mount_point);
2325  if (cvmfs::watchdog_ == NULL) {
2326  *g_boot_error = "failed to initialize watchdog.";
2327  return loader::kFailMonitor;
2328  }
2329  }
2331 
2333  fs_info.type = FileSystem::kFsFuse;
2334  fs_info.name = loader_exports->repository_name;
2335  fs_info.exe_path = loader_exports->program_name;
2336  fs_info.options_mgr = cvmfs::options_mgr_;
2337  fs_info.foreground = loader_exports->foreground;
2339  loader_exports->mount_point, loader_exports->repository_name, fs_info);
2340  if (!cvmfs::file_system_->IsValid()) {
2342  return cvmfs::file_system_->boot_status();
2343  }
2344  if ((cvmfs::file_system_->cache_mgr()->id() == kPosixCacheManager)
2345  && dynamic_cast<PosixCacheManager *>(cvmfs::file_system_->cache_mgr())
2346  ->do_refcount()) {
2347  cvmfs::check_fd_overflow_ = false;
2348  }
2349 
2352  if (!cvmfs::mount_point_->IsValid()) {
2354  return cvmfs::mount_point_->boot_status();
2355  }
2356 
2358 
2360  cvmfs::directory_handles_->set_empty_key((uint64_t)(-1));
2361  cvmfs::directory_handles_->set_deleted_key((uint64_t)(-2));
2362 
2363  LogCvmfs(kLogCvmfs, kLogDebug, "fuse inode size is %lu bits",
2364  sizeof(fuse_ino_t) * 8);
2365 
2369  ->inode_annotation()
2370  ->GetGeneration();
2371  LogCvmfs(kLogCvmfs, kLogDebug, "root inode is %" PRIu64,
2372  uint64_t(cvmfs::mount_point_->catalog_mgr()->GetRootInode()));
2373 
2374  void **channel_or_session = NULL;
2375  if (loader_exports->version >= 4) {
2376  channel_or_session = loader_exports->fuse_channel_or_session;
2377  }
2378 
2379  bool fuse_notify_invalidation = true;
2380  std::string buf;
2381  if (cvmfs::options_mgr_->GetValue("CVMFS_FUSE_NOTIFY_INVALIDATION", &buf)) {
2382  if (!cvmfs::options_mgr_->IsOn(buf)) {
2383  fuse_notify_invalidation = false;
2385  }
2386  }
2388  cvmfs::mount_point_, &cvmfs::inode_generation_info_, channel_or_session,
2389  fuse_notify_invalidation);
2390 
2391  // Control & command interface
2393  cvmfs::mount_point_->talk_socket_path(),
2396  if ((cvmfs::mount_point_->talk_socket_uid() != 0)
2397  || (cvmfs::mount_point_->talk_socket_gid() != 0)) {
2398  uid_t tgt_uid = cvmfs::mount_point_->talk_socket_uid();
2399  gid_t tgt_gid = cvmfs::mount_point_->talk_socket_gid();
2400  int rvi = chown(cvmfs::mount_point_->talk_socket_path().c_str(), tgt_uid,
2401  tgt_gid);
2402  if (rvi != 0) {
2403  *g_boot_error = std::string("failed to set talk socket ownership - ")
2404  + "target " + StringifyInt(tgt_uid) + ":"
2405  + StringifyInt(tgt_uid) + ", user "
2406  + StringifyInt(geteuid()) + ":" + StringifyInt(getegid());
2407  return loader::kFailTalk;
2408  }
2409  }
2410  if (cvmfs::talk_mgr_ == NULL) {
2411  *g_boot_error = "failed to initialize talk socket (" + StringifyInt(errno)
2412  + ")";
2413  return loader::kFailTalk;
2414  }
2415 
2416  // Notification system client
2417  {
2419  if (options->IsDefined("CVMFS_NOTIFICATION_SERVER")) {
2420  std::string config;
2421  options->GetValue("CVMFS_NOTIFICATION_SERVER", &config);
2422  const std::string repo_name = cvmfs::mount_point_->fqrn();
2424  config, repo_name, cvmfs::fuse_remounter_,
2425  cvmfs::mount_point_->download_mgr(),
2427  }
2428  }
2429 
2430  return loader::kFailOk;
2431 }
2432 
2433 
2437 static void Spawn() {
2438  // First thing: kick off the watchdog while we still have a single-threaded
2439  // well-defined state
2440  cvmfs::pid_ = getpid();
2441  if (cvmfs::watchdog_) {
2443  + cvmfs::mount_point_->fqrn());
2444  }
2445 
2447  if (cvmfs::mount_point_->dentry_tracker()->is_active()) {
2449  // Usually every minute
2450  static_cast<unsigned int>(cvmfs::mount_point_->kcache_timeout_sec()));
2451  }
2452 
2455  if (cvmfs::mount_point_->resolv_conf_watcher() != NULL) {
2457  }
2459  quota_mgr->Spawn();
2460  if (quota_mgr->HasCapability(QuotaManager::kCapListeners)) {
2462  quota_mgr, cvmfs::mount_point_->uuid()->uuid() + "-watchdog");
2464  quota_mgr,
2465  cvmfs::mount_point_->catalog_mgr(),
2466  cvmfs::mount_point_->uuid()->uuid() + "-unpin");
2467  }
2470 
2471  if (cvmfs::notification_client_ != NULL) {
2473  }
2474 
2475  if (cvmfs::file_system_->nfs_maps() != NULL) {
2477  }
2478 
2480 
2481  if (cvmfs::mount_point_->telemetry_aggr() != NULL) {
2483  }
2484 }
2485 
2486 
2487 static string GetErrorMsg() {
2488  if (g_boot_error)
2489  return *g_boot_error;
2490  return "";
2491 }
2492 
2493 
2499 static void ShutdownMountpoint() {
2500  delete cvmfs::talk_mgr_;
2501  cvmfs::talk_mgr_ = NULL;
2502 
2505 
2506  // The remonter has a reference to the mount point and the inode generation
2507  delete cvmfs::fuse_remounter_;
2508  cvmfs::fuse_remounter_ = NULL;
2509 
2510  // The unpin listener requires the catalog, so this must be unregistered
2511  // before the catalog manager is removed
2512  if (cvmfs::unpin_listener_ != NULL) {
2514  cvmfs::unpin_listener_ = NULL;
2515  }
2516  if (cvmfs::watchdog_listener_ != NULL) {
2519  }
2520 
2522  delete cvmfs::mount_point_;
2524  cvmfs::mount_point_ = NULL;
2525 }
2526 
2527 
2528 static void Fini() {
2530 
2531  delete cvmfs::file_system_;
2532  delete cvmfs::options_mgr_;
2533  cvmfs::file_system_ = NULL;
2534  cvmfs::options_mgr_ = NULL;
2535 
2536  delete cvmfs::watchdog_;
2537  cvmfs::watchdog_ = NULL;
2538 
2539  delete g_boot_error;
2540  g_boot_error = NULL;
2542 
2544 }
2545 
2546 
2547 static int AltProcessFlavor(int argc, char **argv) {
2548  if (strcmp(argv[1], "__cachemgr__") == 0) {
2549  return PosixQuotaManager::MainCacheManager(argc, argv);
2550  }
2551  if (strcmp(argv[1], "__wpad__") == 0) {
2552  return download::MainResolveProxyDescription(argc, argv);
2553  }
2554  return 1;
2555 }
2556 
2557 
2558 static bool MaintenanceMode(const int fd_progress) {
2559  SendMsg2Socket(fd_progress, "Entering maintenance mode\n");
2560  string msg_progress = "Draining out kernel caches (";
2562  msg_progress += "up to ";
2563  msg_progress += StringifyInt(static_cast<int>(
2564  cvmfs::mount_point_->kcache_timeout_sec()))
2565  + "s)\n";
2566  SendMsg2Socket(fd_progress, msg_progress);
2568  return true;
2569 }
2570 
2571 
2572 static bool SaveState(const int fd_progress, loader::StateList *saved_states) {
2573  string msg_progress;
2574 
2575  unsigned num_open_dirs = cvmfs::directory_handles_->size();
2576  if (num_open_dirs != 0) {
2577 #ifdef DEBUGMSG
2578  for (cvmfs::DirectoryHandles::iterator
2579  i = cvmfs::directory_handles_->begin(),
2580  iEnd = cvmfs::directory_handles_->end();
2581  i != iEnd;
2582  ++i) {
2583  LogCvmfs(kLogCvmfs, kLogDebug, "saving dirhandle %lu", i->first);
2584  }
2585 #endif
2586 
2587  msg_progress = "Saving open directory handles ("
2588  + StringifyInt(num_open_dirs) + " handles)\n";
2589  SendMsg2Socket(fd_progress, msg_progress);
2590 
2591  // TODO(jblomer): should rather be saved just in a malloc'd memory block
2592  cvmfs::DirectoryHandles *saved_handles = new cvmfs::DirectoryHandles(
2594  loader::SavedState *save_open_dirs = new loader::SavedState();
2595  save_open_dirs->state_id = loader::kStateOpenDirs;
2596  save_open_dirs->state = saved_handles;
2597  saved_states->push_back(save_open_dirs);
2598  }
2599 
2600  if (!cvmfs::file_system_->IsNfsSource()) {
2601  msg_progress = "Saving inode tracker\n";
2602  SendMsg2Socket(fd_progress, msg_progress);
2603  glue::InodeTracker *saved_inode_tracker = new glue::InodeTracker(
2604  *cvmfs::mount_point_->inode_tracker());
2605  loader::SavedState *state_glue_buffer = new loader::SavedState();
2606  state_glue_buffer->state_id = loader::kStateGlueBufferV4;
2607  state_glue_buffer->state = saved_inode_tracker;
2608  saved_states->push_back(state_glue_buffer);
2609  }
2610 
2611  msg_progress = "Saving negative entry cache\n";
2612  SendMsg2Socket(fd_progress, msg_progress);
2613  glue::DentryTracker *saved_dentry_tracker = new glue::DentryTracker(
2614  *cvmfs::mount_point_->dentry_tracker());
2615  loader::SavedState *state_dentry_tracker = new loader::SavedState();
2616  state_dentry_tracker->state_id = loader::kStateDentryTracker;
2617  state_dentry_tracker->state = saved_dentry_tracker;
2618  saved_states->push_back(state_dentry_tracker);
2619 
2620  msg_progress = "Saving page cache entry tracker\n";
2621  SendMsg2Socket(fd_progress, msg_progress);
2622  glue::PageCacheTracker *saved_page_cache_tracker = new glue::PageCacheTracker(
2623  *cvmfs::mount_point_->page_cache_tracker());
2624  loader::SavedState *state_page_cache_tracker = new loader::SavedState();
2625  state_page_cache_tracker->state_id = loader::kStatePageCacheTracker;
2626  state_page_cache_tracker->state = saved_page_cache_tracker;
2627  saved_states->push_back(state_page_cache_tracker);
2628 
2629  msg_progress = "Saving chunk tables\n";
2630  SendMsg2Socket(fd_progress, msg_progress);
2631  ChunkTables *saved_chunk_tables = new ChunkTables(
2632  *cvmfs::mount_point_->chunk_tables());
2633  loader::SavedState *state_chunk_tables = new loader::SavedState();
2634  state_chunk_tables->state_id = loader::kStateOpenChunksV4;
2635  state_chunk_tables->state = saved_chunk_tables;
2636  saved_states->push_back(state_chunk_tables);
2637 
2638  msg_progress = "Saving inode generation\n";
2639  SendMsg2Socket(fd_progress, msg_progress);
2643  *saved_inode_generation = new cvmfs::InodeGenerationInfo(
2645  loader::SavedState *state_inode_generation = new loader::SavedState();
2646  state_inode_generation->state_id = loader::kStateInodeGeneration;
2647  state_inode_generation->state = saved_inode_generation;
2648  saved_states->push_back(state_inode_generation);
2649 
2650  msg_progress = "Saving fuse state\n";
2651  SendMsg2Socket(fd_progress, msg_progress);
2652  cvmfs::FuseState *saved_fuse_state = new cvmfs::FuseState();
2653  saved_fuse_state->cache_symlinks = cvmfs::mount_point_->cache_symlinks();
2654  saved_fuse_state->has_dentry_expire = cvmfs::mount_point_
2655  ->fuse_expire_entry();
2656  loader::SavedState *state_fuse = new loader::SavedState();
2657  state_fuse->state_id = loader::kStateFuse;
2658  state_fuse->state = saved_fuse_state;
2659  saved_states->push_back(state_fuse);
2660 
2661  // Close open file catalogs
2663 
2664  loader::SavedState *state_cache_mgr = new loader::SavedState();
2665  state_cache_mgr->state_id = loader::kStateOpenFiles;
2666  state_cache_mgr->state = cvmfs::file_system_->cache_mgr()->SaveState(
2667  fd_progress);
2668  saved_states->push_back(state_cache_mgr);
2669 
2670  msg_progress = "Saving open files counter\n";
2671  uint32_t *saved_num_fd = new uint32_t(
2672  cvmfs::file_system_->no_open_files()->Get());
2673  loader::SavedState *state_num_fd = new loader::SavedState();
2674  state_num_fd->state_id = loader::kStateOpenFilesCounter;
2675  state_num_fd->state = saved_num_fd;
2676  saved_states->push_back(state_num_fd);
2677 
2678  return true;
2679 }
2680 
2681 
2682 static bool RestoreState(const int fd_progress,
2683  const loader::StateList &saved_states) {
2684  // If we have no saved version of the page cache tracker, it is unsafe
2685  // to start using it. The page cache tracker has to run for the entire
2686  // lifetime of the mountpoint or not at all.
2688 
2689  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2690  if (saved_states[i]->state_id == loader::kStateOpenDirs) {
2691  SendMsg2Socket(fd_progress, "Restoring open directory handles... ");
2694  *saved_handles = (cvmfs::DirectoryHandles *)saved_states[i]->state;
2695  cvmfs::directory_handles_ = new cvmfs::DirectoryHandles(*saved_handles);
2698  cvmfs::DirectoryHandles::const_iterator i = cvmfs::directory_handles_
2699  ->begin();
2700  for (; i != cvmfs::directory_handles_->end(); ++i) {
2701  if (i->first >= cvmfs::next_directory_handle_)
2702  cvmfs::next_directory_handle_ = i->first + 1;
2703  }
2704 
2706  fd_progress,
2707  StringifyInt(cvmfs::directory_handles_->size()) + " handles\n");
2708  }
2709 
2710  if (saved_states[i]->state_id == loader::kStateGlueBuffer) {
2711  SendMsg2Socket(fd_progress, "Migrating inode tracker (v1 to v4)... ");
2713  *saved_inode_tracker = (compat::inode_tracker::InodeTracker *)
2714  saved_states[i]
2715  ->state;
2716  compat::inode_tracker::Migrate(saved_inode_tracker,
2717  cvmfs::mount_point_->inode_tracker());
2718  SendMsg2Socket(fd_progress, " done\n");
2719  }
2720 
2721  if (saved_states[i]->state_id == loader::kStateGlueBufferV2) {
2722  SendMsg2Socket(fd_progress, "Migrating inode tracker (v2 to v4)... ");
2724  *saved_inode_tracker = (compat::inode_tracker_v2::InodeTracker *)
2725  saved_states[i]
2726  ->state;
2727  compat::inode_tracker_v2::Migrate(saved_inode_tracker,
2728  cvmfs::mount_point_->inode_tracker());
2729  SendMsg2Socket(fd_progress, " done\n");
2730  }
2731 
2732  if (saved_states[i]->state_id == loader::kStateGlueBufferV3) {
2733  SendMsg2Socket(fd_progress, "Migrating inode tracker (v3 to v4)... ");
2735  *saved_inode_tracker = (compat::inode_tracker_v3::InodeTracker *)
2736  saved_states[i]
2737  ->state;
2738  compat::inode_tracker_v3::Migrate(saved_inode_tracker,
2739  cvmfs::mount_point_->inode_tracker());
2740  SendMsg2Socket(fd_progress, " done\n");
2741  }
2742 
2743  if (saved_states[i]->state_id == loader::kStateGlueBufferV4) {
2744  SendMsg2Socket(fd_progress, "Restoring inode tracker... ");
2747  *saved_inode_tracker = (glue::InodeTracker *)saved_states[i]->state;
2749  glue::InodeTracker(*saved_inode_tracker);
2750  SendMsg2Socket(fd_progress, " done\n");
2751  }
2752 
2753  if (saved_states[i]->state_id == loader::kStateDentryTracker) {
2754  SendMsg2Socket(fd_progress, "Restoring dentry tracker... ");
2757  *saved_dentry_tracker = static_cast<glue::DentryTracker *>(
2758  saved_states[i]->state);
2760  glue::DentryTracker(*saved_dentry_tracker);
2761  SendMsg2Socket(fd_progress, " done\n");
2762  }
2763 
2764  if (saved_states[i]->state_id == loader::kStatePageCacheTracker) {
2765  SendMsg2Socket(fd_progress, "Restoring page cache entry tracker... ");
2768  *saved_page_cache_tracker = (glue::PageCacheTracker *)saved_states[i]
2769  ->state;
2771  glue::PageCacheTracker(*saved_page_cache_tracker);
2772  SendMsg2Socket(fd_progress, " done\n");
2773  }
2774 
2775  ChunkTables *chunk_tables = cvmfs::mount_point_->chunk_tables();
2776 
2777  if (saved_states[i]->state_id == loader::kStateOpenChunks) {
2778  SendMsg2Socket(fd_progress, "Migrating chunk tables (v1 to v4)... ");
2780  *saved_chunk_tables = (compat::chunk_tables::ChunkTables *)
2781  saved_states[i]
2782  ->state;
2783  compat::chunk_tables::Migrate(saved_chunk_tables, chunk_tables);
2785  fd_progress,
2786  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2787  }
2788 
2789  if (saved_states[i]->state_id == loader::kStateOpenChunksV2) {
2790  SendMsg2Socket(fd_progress, "Migrating chunk tables (v2 to v4)... ");
2792  *saved_chunk_tables = (compat::chunk_tables_v2::ChunkTables *)
2793  saved_states[i]
2794  ->state;
2795  compat::chunk_tables_v2::Migrate(saved_chunk_tables, chunk_tables);
2797  fd_progress,
2798  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2799  }
2800 
2801  if (saved_states[i]->state_id == loader::kStateOpenChunksV3) {
2802  SendMsg2Socket(fd_progress, "Migrating chunk tables (v3 to v4)... ");
2804  *saved_chunk_tables = (compat::chunk_tables_v3::ChunkTables *)
2805  saved_states[i]
2806  ->state;
2807  compat::chunk_tables_v3::Migrate(saved_chunk_tables, chunk_tables);
2809  fd_progress,
2810  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2811  }
2812 
2813  if (saved_states[i]->state_id == loader::kStateOpenChunksV4) {
2814  SendMsg2Socket(fd_progress, "Restoring chunk tables... ");
2815  chunk_tables->~ChunkTables();
2816  ChunkTables *saved_chunk_tables = reinterpret_cast<ChunkTables *>(
2817  saved_states[i]->state);
2818  new (chunk_tables) ChunkTables(*saved_chunk_tables);
2819  SendMsg2Socket(fd_progress, " done\n");
2820  }
2821 
2822  if (saved_states[i]->state_id == loader::kStateInodeGeneration) {
2823  SendMsg2Socket(fd_progress, "Restoring inode generation... ");
2825  *old_info = (cvmfs::InodeGenerationInfo *)saved_states[i]->state;
2826  if (old_info->version == 1) {
2827  // Migration
2829  ->initial_revision;
2831  // Note: in the rare case of inode generation being 0 before, inode
2832  // can clash after reload before remount
2833  } else {
2834  cvmfs::inode_generation_info_ = *old_info;
2835  }
2837  SendMsg2Socket(fd_progress, " done\n");
2838  }
2839 
2840  if (saved_states[i]->state_id == loader::kStateOpenFilesCounter) {
2841  SendMsg2Socket(fd_progress, "Restoring open files counter... ");
2843  *(reinterpret_cast<uint32_t *>(saved_states[i]->state)));
2844  SendMsg2Socket(fd_progress, " done\n");
2845  }
2846 
2847  if (saved_states[i]->state_id == loader::kStateOpenFiles) {
2848  int old_root_fd = cvmfs::mount_point_->catalog_mgr()->root_fd();
2849 
2850  // TODO(jblomer): make this less hacky
2851 
2853  saved_states[i]->state);
2854  int fixup_root_fd = -1;
2855 
2856  if ((saved_type == kStreamingCacheManager)
2857  && (cvmfs::file_system_->cache_mgr()->id()
2858  != kStreamingCacheManager)) {
2859  // stick to the streaming cache manager
2860  StreamingCacheManager *new_cache_mgr = new StreamingCacheManager(
2862  cvmfs::file_system_->cache_mgr(),
2863  cvmfs::mount_point_->download_mgr(),
2864  cvmfs::mount_point_->external_download_mgr(),
2866  cvmfs::file_system_->statistics());
2867  fixup_root_fd = new_cache_mgr->PlantFd(old_root_fd);
2868  cvmfs::file_system_->ReplaceCacheManager(new_cache_mgr);
2869  cvmfs::mount_point_->fetcher()->ReplaceCacheManager(new_cache_mgr);
2871  new_cache_mgr);
2872  }
2873 
2874  if ((cvmfs::file_system_->cache_mgr()->id() == kStreamingCacheManager)
2875  && (saved_type != kStreamingCacheManager)) {
2876  // stick to the cache manager wrapped into the streaming cache
2877  CacheManager *wrapped_cache_mgr = dynamic_cast<StreamingCacheManager *>(
2879  ->MoveOutBackingCacheMgr(
2880  &fixup_root_fd);
2881  delete cvmfs::file_system_->cache_mgr();
2882  cvmfs::file_system_->ReplaceCacheManager(wrapped_cache_mgr);
2883  cvmfs::mount_point_->fetcher()->ReplaceCacheManager(wrapped_cache_mgr);
2885  wrapped_cache_mgr);
2886  }
2887 
2888  int new_root_fd = cvmfs::file_system_->cache_mgr()->RestoreState(
2889  fd_progress, saved_states[i]->state);
2890  LogCvmfs(kLogCvmfs, kLogDebug, "new root file catalog descriptor @%d",
2891  new_root_fd);
2892  if (new_root_fd >= 0) {
2893  cvmfs::file_system_->RemapCatalogFd(old_root_fd, new_root_fd);
2894  } else if (fixup_root_fd >= 0) {
2896  "new root file catalog descriptor (fixup) @%d", fixup_root_fd);
2897  cvmfs::file_system_->RemapCatalogFd(old_root_fd, fixup_root_fd);
2898  }
2899  }
2900 
2901  if (saved_states[i]->state_id == loader::kStateFuse) {
2902  SendMsg2Socket(fd_progress, "Restoring fuse state... ");
2903  cvmfs::FuseState *fuse_state = static_cast<cvmfs::FuseState *>(
2904  saved_states[i]->state);
2905  if (!fuse_state->cache_symlinks)
2907  if (fuse_state->has_dentry_expire)
2909  SendMsg2Socket(fd_progress, " done\n");
2910  }
2911  }
2912  if (cvmfs::mount_point_->inode_annotation()) {
2913  uint64_t saved_generation = cvmfs::inode_generation_info_.inode_generation;
2914  cvmfs::mount_point_->inode_annotation()->IncGeneration(saved_generation);
2915  }
2916 
2917  return true;
2918 }
2919 
2920 
2921 static void FreeSavedState(const int fd_progress,
2922  const loader::StateList &saved_states) {
2923  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2924  switch (saved_states[i]->state_id) {
2926  SendMsg2Socket(fd_progress, "Releasing saved open directory handles\n");
2927  delete static_cast<cvmfs::DirectoryHandles *>(saved_states[i]->state);
2928  break;
2930  SendMsg2Socket(fd_progress,
2931  "Releasing saved glue buffer (version 1)\n");
2932  delete static_cast<compat::inode_tracker::InodeTracker *>(
2933  saved_states[i]->state);
2934  break;
2936  SendMsg2Socket(fd_progress,
2937  "Releasing saved glue buffer (version 2)\n");
2938  delete static_cast<compat::inode_tracker_v2::InodeTracker *>(
2939  saved_states[i]->state);
2940  break;
2942  SendMsg2Socket(fd_progress,
2943  "Releasing saved glue buffer (version 3)\n");
2944  delete static_cast<compat::inode_tracker_v3::InodeTracker *>(
2945  saved_states[i]->state);
2946  break;
2948  SendMsg2Socket(fd_progress, "Releasing saved glue buffer\n");
2949  delete static_cast<glue::InodeTracker *>(saved_states[i]->state);
2950  break;
2952  SendMsg2Socket(fd_progress, "Releasing saved dentry tracker\n");
2953  delete static_cast<glue::DentryTracker *>(saved_states[i]->state);
2954  break;
2956  SendMsg2Socket(fd_progress, "Releasing saved page cache entry cache\n");
2957  delete static_cast<glue::PageCacheTracker *>(saved_states[i]->state);
2958  break;
2960  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 1)\n");
2961  delete static_cast<compat::chunk_tables::ChunkTables *>(
2962  saved_states[i]->state);
2963  break;
2965  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 2)\n");
2966  delete static_cast<compat::chunk_tables_v2::ChunkTables *>(
2967  saved_states[i]->state);
2968  break;
2970  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 3)\n");
2971  delete static_cast<compat::chunk_tables_v3::ChunkTables *>(
2972  saved_states[i]->state);
2973  break;
2975  SendMsg2Socket(fd_progress, "Releasing chunk tables\n");
2976  delete static_cast<ChunkTables *>(saved_states[i]->state);
2977  break;
2979  SendMsg2Socket(fd_progress, "Releasing saved inode generation info\n");
2980  delete static_cast<cvmfs::InodeGenerationInfo *>(
2981  saved_states[i]->state);
2982  break;
2984  cvmfs::file_system_->cache_mgr()->FreeState(fd_progress,
2985  saved_states[i]->state);
2986  break;
2988  SendMsg2Socket(fd_progress, "Releasing open files counter\n");
2989  delete static_cast<uint32_t *>(saved_states[i]->state);
2990  break;
2991  case loader::kStateFuse:
2992  SendMsg2Socket(fd_progress, "Releasing fuse state\n");
2993  delete static_cast<cvmfs::FuseState *>(saved_states[i]->state);
2994  break;
2995  default:
2996  break;
2997  }
2998  }
2999 }
3000 
3001 
3002 static void __attribute__((constructor)) LibraryMain() {
3003  g_cvmfs_exports = new loader::CvmfsExports();
3004  g_cvmfs_exports->so_version = CVMFS_VERSION;
3005  g_cvmfs_exports->fnAltProcessFlavor = AltProcessFlavor;
3006  g_cvmfs_exports->fnInit = Init;
3007  g_cvmfs_exports->fnSpawn = Spawn;
3008  g_cvmfs_exports->fnFini = Fini;
3009  g_cvmfs_exports->fnGetErrorMsg = GetErrorMsg;
3010  g_cvmfs_exports->fnMaintenanceMode = MaintenanceMode;
3011  g_cvmfs_exports->fnSaveState = SaveState;
3012  g_cvmfs_exports->fnRestoreState = RestoreState;
3013  g_cvmfs_exports->fnFreeSavedState = FreeSavedState;
3014  cvmfs::SetCvmfsOperations(&g_cvmfs_exports->cvmfs_operations);
3015 }
3016 
3017 
3018 static void __attribute__((destructor)) LibraryExit() {
3019  delete g_cvmfs_exports;
3020  g_cvmfs_exports = NULL;
3021 }
bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
Definition: glue_buffer.h:1005
perf::Counter * n_eio_total()
Definition: mountpoint.h:238
bool cache_symlinks()
Definition: mountpoint.h:525
VfsPutRaii GetVfsPutRaii()
Definition: glue_buffer.h:672
std::string repository_name
Definition: loader.h:177
OptionsManager * options_mgr()
Definition: mountpoint.h:248
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:235
bool IsExternalFile() const
std::string mount_point
Definition: loader.h:178
perf::Counter * n_eio_05()
Definition: mountpoint.h:243
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
void UnregisterQuotaListener()
Definition: cvmfs.cc:2119
static const time_t kIndefiniteDeadline
Definition: mountpoint.h:493
perf::Counter * n_fs_inode_replace()
Definition: mountpoint.h:223
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:755
std::string ListKeysPosix(const std::string &merge_with) const
Definition: xattr.cc:138
perf::Counter * n_eio_01()
Definition: mountpoint.h:239
FileSystem * file_system()
Definition: mountpoint.h:521
void TryFinish(const shash::Any &root_hash=shash::Any())
bool InsertNegative(const shash::Md5 &hash)
Definition: lru_md.h:118
Log2Histogram * hist_fs_opendir()
Definition: mountpoint.h:214
time_t catalogs_valid_until_
Definition: cvmfs.cc:2164
static bool HasDifferentContent(const catalog::DirectoryEntry &dirent, const shash::Any &hash, const struct stat &info)
Definition: cvmfs.cc:275
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:722
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:1364
time_t mtime() const
static double GetKcacheTimeout()
Definition: cvmfs.cc:221
void EnterMaintenanceMode()
void set_inode(const inode_t inode)
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:136
loader::Failures boot_status()
Definition: mountpoint.h:77
static void FreeSavedState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2921
int PlantFd(int fd_in_cache_mgr)
bool IsDirectory() const
static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino)
Definition: cvmfs.cc:1623
off_t range_offset
Definition: cache.h:127
bool PrepareValueFencedProtected(gid_t gid)
Definition: magic_xattr.cc:161
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:493
cvmfs::Fetcher * fetcher()
Definition: mountpoint.h:516
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:183
void ShareBuffer(Item **duplicate, bool *large_alloc)
Definition: bigvector.h:77
static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:1015
bool IsChunkedFile() const
FuseRemounter * fuse_remounter_
Definition: cvmfs.cc:135
uid_t talk_socket_uid()
Definition: mountpoint.h:541
int RestoreState(const int fd_progress, void *state)
Definition: cache.cc:182
void Register(const std::string &name, BaseMagicXattr *magic_xattr)
Definition: magic_xattr.cc:139
SmallHashDynamic< uint64_t, uint64_t > handle2uniqino
Definition: file_chunk.h:113
bool Insert(const fuse_ino_t &inode, const catalog::DirectoryEntry &dirent)
Definition: lru_md.h:48
google::dense_hash_map< uint64_t, DirectoryListing, hash_murmur< uint64_t > > DirectoryHandles
Definition: cvmfs.cc:163
#define PANIC(...)
Definition: exception.h:29
void Spawn()
Definition: tracer.cc:207
SpecialDirents GetSpecial() const
bool fuse_expire_entry()
Definition: mountpoint.h:526
virtual void Spawn()=0
static int AltProcessFlavor(int argc, char **argv)
Definition: cvmfs.cc:2547
uint64_t size() const
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
double kcache_timeout_sec()
Definition: mountpoint.h:530
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:653
std::string PrintInodeGeneration()
Definition: cvmfs.cc:244
zlib::Algorithms compression_alg
Definition: file_chunk.h:65
static void SetupLoggingStandalone(const OptionsManager &options_mgr, const std::string &prefix)
Definition: mountpoint.cc:851
int MainResolveProxyDescription(int argc, char **argv)
Definition: wpad.cc:267
bool FindDentry(uint64_t ino, uint64_t *parent_ino, NameString *name)
Definition: glue_buffer.h:700
perf::Counter * n_fs_readlink()
Definition: mountpoint.h:228
std::string fqrn() const
Definition: mountpoint.h:518
unsigned LookupOptions
Definition: catalog_mgr.h:42
void SetMountpoint(const string &mountpoint)
Definition: auto_umount.cc:28
#define CVMFS_TEST_INJECT_BARRIER(...)
Definition: testing.h:62
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:1542
BaseMagicXattr * GetLocked(const std::string &name, PathString path, catalog::DirectoryEntry *d)
Definition: magic_xattr.cc:124
Watchdog * watchdog_
Definition: cvmfs.cc:134
static const size_t kDefaultBufferSize
Definition: cache_stream.h:33
bool Lookup(const shash::Md5 &hash, catalog::DirectoryEntry *dirent, bool update_lru=true)
Definition: lru_md.h:125
lru::InodeCache * inode_cache()
Definition: mountpoint.h:529
NotificationClient * notification_client_
Definition: cvmfs.cc:133
void * SaveState(const int fd_progress)
Definition: cache.cc:211
DirectoryHandles * directory_handles_
Definition: cvmfs.cc:164
StateId state_id
Definition: loader.h:123
CVMFS_EXPORT void CleanupLibcryptoMt()
Definition: crypto_util.cc:66
bool check_fd_overflow_
Definition: cvmfs.cc:174
const shash::Any & content_hash() const
Definition: file_chunk.h:37
time_t catalogs_valid_until()
Definition: fuse_remount.h:68
static bool SaveState(const int fd_progress, loader::StateList *saved_states)
Definition: cvmfs.cc:2572
inode_t inode() const
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:671
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:131
MagicXattrManager * magic_xattr_mgr()
Definition: mountpoint.h:522
std::string program_name
Definition: loader.h:180
Log2Histogram * hist_fs_read()
Definition: mountpoint.h:218
bool IsDirectIo() const
bool SendFd2Socket(int socket_fd, int passing_fd)
Definition: posix.cc:680
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
void EnableFuseExpireEntry()
Definition: mountpoint.cc:1236
static bool RestoreState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2682
virtual uint64_t GetSize()=0
perf::Counter * n_fs_read()
Definition: mountpoint.h:227
bool Lookup(const fuse_ino_t &inode, catalog::DirectoryEntry *dirent, bool update_lru=true)
Definition: lru_md.h:56
void UmountOnCrash()
Definition: auto_umount.cc:38
int fd
Definition: file_chunk.h:76
std::string StringifyUint(const uint64_t value)
Definition: string.cc:83
lru::Md5PathCache * md5path_cache()
Definition: mountpoint.h:531
shash::Any checksum() const
void ParseDefault(const std::string &fqrn)
Definition: options.cc:281
SmallHashDynamic< uint64_t, ChunkFd > handle2fd
Definition: file_chunk.h:114
bool VfsPut(const uint64_t inode, const uint32_t by)
Definition: glue_buffer.h:596
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:109
bool has_dentry_expire
Definition: cvmfs.cc:205
bool has_membership_req()
Definition: mountpoint.h:523
virtual bool PrepareValueFenced()
Definition: cvmfs.cc:2166
unsigned int mode() const
struct cvmcache_object_info __attribute__
Definition: atomic.h:24
const unsigned kLookupDefault
Definition: catalog_mgr.h:43
void RemapCatalogFd(int from, int to)
Definition: mountpoint.cc:1121
MagicXattrMode
Definition: magic_xattr.h:32
bool Pin(const string &path)
Definition: cvmfs.cc:1941
static void cvmfs_destroy(void *unused __attribute__((unused)))
Definition: cvmfs.cc:2085
perf::Counter * n_eio_08()
Definition: mountpoint.h:246
static TalkManager * Create(const std::string &socket_path, MountPoint *mount_point, FuseRemounter *remounter)
Definition: talk.cc:79
unsigned max_open_files_
Definition: cvmfs.cc:168
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:54
perf::Counter * n_fs_open()
Definition: mountpoint.h:226
bool IsStale(const catalog::DirectoryEntry &dirent)
Definition: glue_buffer.h:1025
static void cvmfs_init(void *userdata, struct fuse_conn_info *conn)
Definition: cvmfs.cc:2012
TalkManager * talk_mgr_
Definition: cvmfs.cc:132
const unsigned int kMinOpenFiles
Definition: cvmfs.cc:182
virtual uint64_t GetCapacity()=0
std::string membership_req()
Definition: mountpoint.h:532
void Throttle()
Definition: backoff.cc:48
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:86
uint64_t next_directory_handle_
Definition: cvmfs.cc:166
bool IsValid(const std::string &input) const
Definition: sanitizer.cc:112
void Unlock()
Definition: file_chunk.h:100
static void InitOptionsMgr(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2263
Fence * fence()
Definition: fuse_remount.h:67
const uint64_t cache_timeout()
Definition: mountpoint.h:463
pid_t pid_
Definition: cvmfs.cc:156
void ReplaceCacheManager(CacheManager *new_cache_mgr)
Definition: mountpoint.cc:1125
void DisableCacheSymlinks()
Definition: mountpoint.cc:1230
PathString path
Definition: file_chunk.h:64
static bool GetDirentForInode(const fuse_ino_t ino, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:316
glue::PageCacheTracker * page_cache_tracker()
Definition: mountpoint.h:534
NameString name() const
static Watchdog * Create(FnOnCrash on_crash)
Definition: monitor.cc:67
bool IsLink() const
void SetBit(unsigned int bit, T *field)
Definition: algorithm.h:31
perf::Counter * n_eio_07()
Definition: mountpoint.h:245
BigVector< FileChunk > FileChunkList
Definition: file_chunk.h:47
virtual void Spawn()=0
static bool FixupOpenInode(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:297
bool HasXattrs() const
static void TraceInode(const int event, fuse_ino_t ino, const std::string &msg)
Definition: cvmfs.cc:507
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:507
void Set(const int64_t val)
Definition: statistics.h:33
Log2Histogram * hist_fs_forget_multi()
Definition: mountpoint.h:211
static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
Definition: cvmfs.cc:1852
bool IsRegular() const
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:306
void ClearBit(unsigned int bit, T *field)
Definition: algorithm.h:36
uint64_t size
unzipped size, if known
Definition: cache.h:125
void DoubleCapacity()
Definition: bigvector.h:83
bool platform_getxattr(const std::string &path, const std::string &name, std::string *value)
static bool MayBeInPageCacheTracker(const catalog::DirectoryEntry &dirent)
Definition: cvmfs.cc:270
Log2Histogram * hist_fs_release()
Definition: mountpoint.h:219
pthread_mutex_t * Handle2Lock(const uint64_t handle) const
Definition: file_chunk.cc:139
void Lock()
Definition: file_chunk.h:95
uint64_t FindInode(const PathString &path)
Definition: glue_buffer.h:692
ListenerHandle * RegisterWatchdogListener(QuotaManager *quota_manager, const string &repository_name)
perf::Counter * n_emfile()
Definition: mountpoint.h:247
const loader::LoaderExports * loader_exports_
Definition: cvmfs.cc:154
int Fetch(const CacheManager::LabeledObject &object, const std::string &alt_url="")
Definition: fetch.cc:82
EVisibility visibility()
Definition: magic_xattr.h:255
virtual inode_t GetGeneration()=0
std::string config_files
Definition: loader.h:179
perf::Counter * no_open_dirs()
Definition: mountpoint.h:236
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:527
CacheManagerIds
Definition: cache.h:24
Log2Histogram * hist_fs_releasedir()
Definition: mountpoint.h:215
quota::ListenerHandle * watchdog_listener_
Definition: cvmfs.cc:157
void Add(const uint64_t inode_parent, const char *name, uint64_t timeout_s)
Definition: glue_buffer.h:833
virtual bool IsCanceled()
Definition: cvmfs.cc:189
void SetSize(const size_t new_size)
Definition: bigvector.h:112
std::string boot_error()
Definition: mountpoint.h:78
off_t offset() const
Definition: file_chunk.h:38
lru::PathCache * path_cache()
Definition: mountpoint.h:535
struct statvfs * info()
Definition: mountpoint.h:464
static void cvmfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:1112
zlib::Algorithms compression_algorithm() const
bool IsNfsSource()
Definition: mountpoint.h:195
CacheManager * cache_mgr()
Definition: mountpoint.h:205
const signature::SignatureManager * signature_mgr() const
Definition: repository.h:121
file_watcher::FileWatcher * resolv_conf_watcher()
Definition: mountpoint.h:513
unsigned chunk_idx
Definition: file_chunk.h:77
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)
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:145
void SpawnCleaner(unsigned interval_s)
Definition: glue_buffer.cc:165
static void ShutdownMountpoint()
Definition: cvmfs.cc:2499
static bool AssertOrLog(int t, const LogSource, const int, const char *,...)
Definition: exception.h:60
static bool IncAndCheckNoOpenFiles()
Definition: cvmfs.cc:214
unsigned FindChunkIdx(const uint64_t offset)
Definition: file_chunk.cc:23
virtual void FinalizeValue()
Definition: cvmfs.cc:2191
perf::TelemetryAggregator * telemetry_aggr()
Definition: mountpoint.h:539
bool SendFuseFd(const std::string &socket_path)
Definition: cvmfs.cc:2130
perf::Counter * no_open_files()
Definition: mountpoint.h:237
AuthzSessionManager * authz_session_mgr()
Definition: mountpoint.h:505
LinkString symlink() const
void Insert(const Key &key, const Value &value)
Definition: smallhash.h:106
IoErrorInfo * io_error_info()
Definition: mountpoint.h:233
download::DownloadManager * download_mgr()
Definition: mountpoint.h:509
perf::Counter * n_fs_stat_stale()
Definition: mountpoint.h:230
void ReplaceCacheManager(CacheManager *new_cache_mgr)
Definition: fetch.h:116
static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations)
Definition: cvmfs.cc:2093
bool simple_options_parsing
Definition: loader.h:188
static const int kLabelPinned
Definition: cache.h:81
perf::Counter * n_fs_forget()
Definition: mountpoint.h:222
Log2Histogram * hist_fs_readlink()
Definition: mountpoint.h:213
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2309
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:77
SmallHashDynamic< uint64_t, uint32_t > inode2references
Definition: file_chunk.h:119
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:824
size_t capacity() const
Definition: bigvector.h:118
static void ReplyNegative(const catalog::DirectoryEntry &dirent, fuse_req_t req)
Definition: cvmfs.cc:732
void Inc(class Counter *counter)
Definition: statistics.h:50
virtual int Close(int fd)=0
CacheManagerIds PeekState(void *state)
Definition: cache.h:205
bool GetValue(const std::string &key, std::string *value) const
Definition: options.cc:368
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:1064
ChunkTables * chunk_tables()
Definition: mountpoint.h:508
const unsigned kLookupRawSymlink
Definition: catalog_mgr.h:44
pthread_mutex_t lock_directory_handles_
Definition: cvmfs.cc:165
static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:881
OptionsManager * options_mgr_
Definition: cvmfs.cc:155
virtual bool GetPath(const uint64_t inode, PathString *path)=0
unsigned version
Definition: cvmfs.cc:203
shash::Algorithms hash_algorithm() const
glue::DentryTracker * dentry_tracker()
Definition: mountpoint.h:533
static int MainCacheManager(int argc, char **argv)
perf::Counter * n_fs_dir_open()
Definition: mountpoint.h:221
Log2Histogram * hist_fs_readdir()
Definition: mountpoint.h:216
cvmfs::Fetcher * external_fetcher()
Definition: mountpoint.h:520
bool enforce_acls()
Definition: mountpoint.h:524
static const int kLabelVolatile
Definition: cache.h:82
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
static bool CheckVoms(const fuse_ctx &fctx)
Definition: cvmfs.cc:255
static void FillOpenFlags(const glue::PageCacheTracker::OpenDirectives od, struct fuse_file_info *fi)
Definition: cvmfs.cc:1089
void FreeState(const int fd_progress, void *state)
Definition: cache.cc:95
static FileSystem * Create(const FileSystemInfo &fs_info)
Definition: mountpoint.cc:164
bool IsEmpty() const
Definition: bigvector.h:70
bool cache_symlinks
Definition: cvmfs.cc:204
zlib::Algorithms zip_algorithm
Definition: cache.h:126
bool Contains(const Key &key) const
Definition: smallhash.h:99
Log2Histogram * hist_fs_open()
Definition: mountpoint.h:217
OpenDirectives Open(uint64_t inode, const shash::Any &hash, const struct stat &info)
Definition: glue_buffer.cc:309
pthread_mutex_t * lock()
Definition: mountpoint.h:465
std::string ToString() const
Definition: shortstring.h:139
QuotaManager * quota_mgr()
Definition: cache.h:191
static string GetErrorMsg()
Definition: cvmfs.cc:2487
static uint64_t GetDirentForPath(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:406
int ConnectSocket(const std::string &path)
Definition: posix.cc:422
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:277
virtual void Spawn()
Definition: nfs_maps.h:36
Log2Histogram * hist_fs_lookup()
Definition: mountpoint.h:209
bool IsCompatibleFileType(unsigned mode) const
Definition: glue_buffer.h:85
uint64_t String2Uint64(const string &value)
Definition: string.cc:240
bool TestBit(unsigned int bit, const T field)
Definition: algorithm.h:41
CVMFS_EXPORT void SetupLibcryptoMt()
Definition: crypto_util.cc:48
void Close(uint64_t inode)
Definition: glue_buffer.cc:397
string * g_boot_error
Definition: cvmfs.cc:2154
virtual void FinalizeValue()
Definition: cvmfs.cc:2204
bool ListFileChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks)
static void Fini()
Definition: cvmfs.cc:2528
perf::Counter * n_fs_lookup()
Definition: mountpoint.h:224
static void Spawn()
Definition: cvmfs.cc:2437
static bool UseWatchdog()
Definition: cvmfs.cc:234
void Migrate(InodeTracker *old_tracker, glue::InodeTracker *new_tracker)
Definition: compat.cc:79
OpenDirectives OpenDirect()
Definition: glue_buffer.cc:386
ShortString< kDefaultMaxPath, 0 > PathString
Definition: shortstring.h:213
uint32_t size() const
Definition: smallhash.h:291
uint64_t next_handle
Definition: file_chunk.h:120
shash::Any HashChunkList()
Definition: file_chunk.cc:49
size_t size() const
Definition: file_chunk.h:39
FuseInterruptCue(fuse_req_t *r)
Definition: cvmfs.cc:187
void UnregisterListener(ListenerHandle *handle)
Definition: mutex.h:42
bool Evict(const string &path)
Definition: cvmfs.cc:1914
std::vector< SavedState * > StateList
Definition: loader.h:126
perf::Counter * n_eio_02()
Definition: mountpoint.h:240
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:14
static unsigned CheckMaxOpenFiles()
Definition: cvmfs.cc:2283
FileSystem * file_system_
Definition: cvmfs.cc:130
perf::Counter * n_eio_03()
Definition: mountpoint.h:241
EvictRaii GetEvictRaii()
Definition: glue_buffer.h:1059
perf::Counter * n_eio_04()
Definition: mountpoint.h:242
void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode)
Definition: cvmfs.cc:228
bool Erase(const Key &key)
Definition: smallhash.h:112
bool LookupXattrs(const PathString &path, XattrList *xattrs)
perf::Counter * n_fs_lookup_negative()
Definition: mountpoint.h:225
StatfsCache * statfs_cache()
Definition: mountpoint.h:546
std::string path
Definition: cache.h:133
virtual uint64_t GetInode(const PathString &path)=0
void Spawn(const std::string &crash_dump_path)
Definition: monitor.cc:514
virtual void FinalizeValue()
Definition: cvmfs.cc:2171
Log2Histogram * hist_fs_forget()
Definition: mountpoint.h:210
const int kNumReservedFd
Definition: cvmfs.cc:178
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:1048
unsigned GetLength() const
Definition: shortstring.h:131
static const int kLabelExternal
Definition: cache.h:83
virtual void IncGeneration(const uint64_t by)=0
bool Lookup(const Key &key, Value *value) const
Definition: smallhash.h:70
inode_t MangleInode(const inode_t inode) const
Definition: catalog_mgr.h:326
static const int kLabelChunked
Definition: cache.h:84
virtual void FinalizeValue()
Definition: cvmfs.cc:2198
bool Insert(const fuse_ino_t &inode, const PathString &path)
Definition: lru_md.h:79
BackoffThrottle * backoff_throttle()
Definition: mountpoint.h:506
Log2Histogram * hist_fs_getattr()
Definition: mountpoint.h:212
gid_t talk_socket_gid()
Definition: mountpoint.h:542
const char * c_str() const
Definition: shortstring.h:143
static bool GetPathForInode(const fuse_ino_t ino, PathString *path)
Definition: cvmfs.cc:459
static const unsigned int kBitDirectIo
Definition: glue_buffer.h:931
virtual void FinalizeValue()
Definition: cvmfs.cc:2183
const char * GetChars() const
Definition: shortstring.h:123
static void RegisterMagicXattrs()
Definition: cvmfs.cc:2215
bool IsDefined(const std::string &key)
Definition: options.cc:362
std::string GetCurrentWorkingDirectory()
Definition: posix.cc:1067
virtual void Remove(const shash::Any &file)=0
glue::InodeTracker * inode_tracker()
Definition: mountpoint.h:528
bool Insert(const shash::Md5 &hash, const catalog::DirectoryEntry &dirent)
Definition: lru_md.h:110
perf::Counter * n_fs_stat()
Definition: mountpoint.h:229
static void size_t size
Definition: smalloc.h:54
virtual ~FuseInterruptCue()
Definition: cvmfs.cc:188
static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
Definition: cvmfs.cc:519
#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:118
static bool MaintenanceMode(const int fd_progress)
Definition: cvmfs.cc:2558
uint64_t * expiry_deadline()
Definition: mountpoint.h:462
void Migrate(ChunkTables *old_tables,::ChunkTables *new_tables)
Definition: compat.cc:230
FileChunkList * list
Definition: file_chunk.h:63
static FileSystem * InitSystemFs(const string &mount_path, const string &fqrn, FileSystem::FileSystemInfo fs_info)
Definition: cvmfs.cc:2230
static const inode_t kInodeOffset
Definition: catalog_mgr.h:245
void VfsGet(const InodeEx inode_ex, const PathString &path)
Definition: glue_buffer.h:668
const Item * AtPtr(const size_t index) const
Definition: bigvector.h:53
static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
Definition: cvmfs.cc:1688
Tracer * tracer()
Definition: mountpoint.h:544
perf::Counter * n_fs_statfs_cached()
Definition: mountpoint.h:232
bool FindPath(InodeEx *inode_ex, PathString *path)
Definition: glue_buffer.h:674
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:856
size_t size() const
Definition: bigvector.h:117
download::DownloadManager * external_download_mgr()
Definition: mountpoint.h:510
perf::Counter * n_fs_statfs()
Definition: mountpoint.h:231
quota::ListenerHandle * unpin_listener_
Definition: cvmfs.cc:158
static MountPoint * Create(const std::string &fqrn, FileSystem *file_system, OptionsManager *options_mgr=NULL)
Definition: mountpoint.cc:1243
void Spawn()
Definition: talk.cc:951
fuse_req_t * req_ptr_
Definition: cvmfs.cc:192
perf::Counter * n_eio_06()
Definition: mountpoint.h:244
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545
OptionsManager * options_mgr
Definition: mountpoint.h:139