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