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