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