CernVM-FS  2.10.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/hash.h"
76 #include "crypto/signature.h"
77 #include "directory_entry.h"
78 #include "download.h"
79 #include "duplex_fuse.h"
80 #include "fence.h"
81 #include "fetch.h"
82 #include "file_chunk.h"
83 #include "fuse_inode_gen.h"
84 #include "fuse_remount.h"
85 #include "globals.h"
86 #include "glue_buffer.h"
87 #include "history_sqlite.h"
88 #include "interrupt.h"
89 #include "loader.h"
90 #include "lru_md.h"
91 #include "magic_xattr.h"
92 #include "manifest_fetch.h"
93 #include "monitor.h"
94 #include "mountpoint.h"
95 #include "nfs_maps.h"
96 #include "notification_client.h"
97 #include "options.h"
98 #include "quota_listener.h"
99 #include "quota_posix.h"
100 #include "shortstring.h"
101 #include "sqlitemem.h"
102 #include "sqlitevfs.h"
103 #include "statistics.h"
104 #include "talk.h"
105 #include "tracer.h"
106 #include "util/algorithm.h"
107 #include "util/atomic.h"
108 #include "util/concurrency.h"
109 #include "util/exception.h"
110 #include "util/logging.h"
111 #include "util/platform.h"
112 #include "util/smalloc.h"
113 #include "util/uuid.h"
114 #include "wpad.h"
115 #include "xattr.h"
116 
117 using namespace std; // NOLINT
118 
119 namespace cvmfs {
120 
128 
129 
135  char *buffer;
137  // Not really used anymore. But directory listing needs to be migrated during
138  // hotpatch. If buffer is allocated by smmap, capacity is zero.
139  size_t size;
140  size_t capacity;
141 
142  DirectoryListing() : buffer(NULL), size(0), capacity(0) { }
143 };
144 
147 pid_t pid_ = 0;
150 
151 
152 typedef google::dense_hash_map<uint64_t, DirectoryListing,
156 pthread_mutex_t lock_directory_handles_ = PTHREAD_MUTEX_INITIALIZER;
158 
159 unsigned max_open_files_;
163 const int kNumReservedFd = 512;
164 
165 
167  public:
168  explicit FuseInterruptCue(fuse_req_t *r) : req_ptr_(r) { }
169  virtual ~FuseInterruptCue() { }
170  virtual bool IsCanceled() { return fuse_req_interrupted(*req_ptr_); }
171  private:
172  fuse_req_t *req_ptr_;
173 };
174 
175 
176 static inline double GetKcacheTimeout() {
177  if (!fuse_remounter_->IsCaching())
178  return 0.0;
180 }
181 
182 
183 void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode) {
184  *drainout_mode = fuse_remounter_->IsInDrainoutMode();
185  *maintenance_mode = fuse_remounter_->IsInMaintenanceMode();
186 }
187 
188 
189 static bool UseWatchdog() {
190  if (loader_exports_ == NULL || loader_exports_->version < 2) {
191  return true; // spawn watchdog by default
192  // Note: with library versions before 2.1.8 it might not
193  // create stack traces properly in all cases
194  }
195 
197 }
198 
199 std::string PrintInodeGeneration() {
200  return "init-catalog-revision: " +
202  "current-catalog-revision: " +
204  "incarnation: " + StringifyInt(inode_generation_info_.incarnation) + " " +
206  + "\n";
207 }
208 
209 
210 static bool CheckVoms(const fuse_ctx &fctx) {
212  return true;
213  string mreq = mount_point_->membership_req();
214  LogCvmfs(kLogCvmfs, kLogDebug, "Got VOMS authz %s from filesystem "
215  "properties", mreq.c_str());
216 
217  if (fctx.uid == 0)
218  return true;
219 
220  return mount_point_->authz_session_mgr()->IsMemberOf(fctx.pid, mreq);
221 }
222 
224  return dirent.IsRegular() &&
225  (dirent.inode() < mount_point_->catalog_mgr()->GetRootInode());
226 }
227 
229  const catalog::DirectoryEntry &dirent,
230  const shash::Any &hash,
231  const struct stat &info)
232 {
233  if (hash == dirent.checksum())
234  return false;
235  // For chunked files, we don't want to load the full list of chunk hashes
236  // so we only check the last modified timestamp
237  if (dirent.IsChunkedFile() && (info.st_mtime == dirent.mtime()))
238  return false;
239  return true;
240 }
241 
252 static bool FixupOpenInode(const PathString &path,
253  catalog::DirectoryEntry *dirent)
254 {
255  if (!MayBeInPageCacheTracker(*dirent))
256  return false;
257 
258  shash::Any hash_open;
259  struct stat info;
260  bool is_open = mount_point_->page_cache_tracker()->GetInfoIfOpen(
261  dirent->inode(), &hash_open, &info);
262  if (!is_open)
263  return false;
264  if (!HasDifferentContent(*dirent, hash_open, info))
265  return false;
266 
267  // Overwrite dirent with inode from current generation
268  bool found = mount_point_->catalog_mgr()->LookupPath(
269  path, catalog::kLookupSole, dirent);
270  assert(found);
271 
272  return true;
273 }
274 
275 static bool GetDirentForInode(const fuse_ino_t ino,
276  catalog::DirectoryEntry *dirent)
277 {
278  // Lookup inode in cache
279  if (mount_point_->inode_cache()->Lookup(ino, dirent))
280  return true;
281 
282  // Look in the catalogs in 2 steps: lookup inode->path, lookup path
283  static catalog::DirectoryEntry dirent_negative =
285  // Reset directory entry. If the function returns false and dirent is no
286  // the kDirentNegative, it was an I/O error
287  *dirent = catalog::DirectoryEntry();
288 
290 
291  if (file_system_->IsNfsSource()) {
292  // NFS mode
293  PathString path;
294  bool retval = file_system_->nfs_maps()->GetPath(ino, &path);
295  if (!retval) {
296  *dirent = dirent_negative;
297  return false;
298  }
299  if (catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent)) {
300  // Fix inodes
301  dirent->set_inode(ino);
302  mount_point_->inode_cache()->Insert(ino, *dirent);
303  return true;
304  }
305  return false; // Not found in catalog or catalog load error
306  }
307 
308  // Non-NFS mode
309  PathString path;
310  if (ino == catalog_mgr->GetRootInode()) {
311  bool retval =
312  catalog_mgr->LookupPath(PathString(), catalog::kLookupSole, dirent);
313  assert(retval);
314  dirent->set_inode(ino);
315  mount_point_->inode_cache()->Insert(ino, *dirent);
316  return true;
317  }
318 
320  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, &path);
321  if (!retval) {
322  // This may be a retired inode whose stat information is only available
323  // in the page cache tracker because there is still an open file
325  "GetDirentForInode inode lookup failure %" PRId64, ino);
326  *dirent = dirent_negative;
327  // Indicate that the inode was not found in the tracker rather than not
328  // found in the catalog
329  dirent->set_inode(ino);
330  return false;
331  }
332  if (catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent)) {
333  if (!inode_ex.IsCompatibleFileType(dirent->mode())) {
335  "Warning: inode %" PRId64 " (%s) changed file type",
336  ino, path.c_str());
337  // TODO(jblomer): we detect this issue but let it continue unhandled.
338  // Fix me.
339  }
340 
341  // Fix inodes
342  dirent->set_inode(ino);
343  mount_point_->inode_cache()->Insert(ino, *dirent);
344  return true;
345  }
346 
347  // Can happen after reload of catalogs or on catalog load failure
348  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForInode path lookup failure");
349  return false;
350 }
351 
352 
360 static uint64_t GetDirentForPath(const PathString &path,
361  catalog::DirectoryEntry *dirent)
362 {
363  uint64_t live_inode = 0;
364  if (!file_system_->IsNfsSource())
365  live_inode = mount_point_->inode_tracker()->FindInode(path);
366 
367  shash::Md5 md5path(path.GetChars(), path.GetLength());
368  if (mount_point_->md5path_cache()->Lookup(md5path, dirent)) {
369  if (dirent->GetSpecial() == catalog::kDirentNegative)
370  return false;
371  // We may have initially stored the entry with an old inode in the
372  // md5path cache and now should update it with the new one.
373  if (!file_system_->IsNfsSource() && (live_inode != 0))
374  dirent->set_inode(live_inode);
375  return 1;
376  }
377 
379 
380  // Lookup inode in catalog TODO: not twice md5 calculation
381  bool retval;
382  retval = catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent);
383  if (retval) {
384  if (file_system_->IsNfsSource()) {
385  dirent->set_inode(file_system_->nfs_maps()->GetInode(path));
386  mount_point_->md5path_cache()->Insert(md5path, *dirent);
387  } else if (live_inode != 0) {
388  dirent->set_inode(live_inode);
389  if (FixupOpenInode(path, dirent)) {
391  "content of %s change, replacing inode %" PRIu64 " --> %" PRIu64,
392  path.c_str(), live_inode, dirent->inode());
393  return live_inode;
394  // Do not populate the md5path cache until the inode tracker is fixed
395  } else {
396  mount_point_->md5path_cache()->Insert(md5path, *dirent);
397  }
398  }
399  return 1;
400  }
401 
402  LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry");
403  // Only insert ENOENT results into negative cache. Otherwise it was an
404  // error loading nested catalogs
405  if (dirent->GetSpecial() == catalog::kDirentNegative)
407  return 0;
408 }
409 
410 
411 static bool GetPathForInode(const fuse_ino_t ino, PathString *path) {
412  // Check the path cache first
413  if (mount_point_->path_cache()->Lookup(ino, path))
414  return true;
415 
416  if (file_system_->IsNfsSource()) {
417  // NFS mode, just a lookup
418  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - lookup in NFS maps", ino);
419  if (file_system_->nfs_maps()->GetPath(ino, path)) {
420  mount_point_->path_cache()->Insert(ino, *path);
421  return true;
422  }
423  return false;
424  }
425 
426  if (ino == mount_point_->catalog_mgr()->GetRootInode())
427  return true;
428 
429  LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - looking in inode tracker", ino);
431  bool retval = mount_point_->inode_tracker()->FindPath(&inode_ex, path);
432  assert(retval);
433  mount_point_->path_cache()->Insert(ino, *path);
434  return true;
435 }
436 
437 static void DoTraceInode(const int event,
438  fuse_ino_t ino,
439  const std::string &msg)
440 {
441  PathString path;
442  bool found = GetPathForInode(ino, &path);
443  if (!found) {
445  "Tracing: Could not find path for inode %" PRIu64, uint64_t(ino));
446  mount_point_->tracer()->Trace(event, PathString("@UNKNOWN"), msg);
447  } else {
448  mount_point_->tracer()->Trace(event, path, msg);
449  }
450 }
451 
452 static void inline TraceInode(const int event,
453  fuse_ino_t ino,
454  const std::string &msg)
455 {
456  if (mount_point_->tracer()->IsActive()) DoTraceInode(event, ino, msg);
457 }
458 
464 static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
466 
468  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
469  FuseInterruptCue ic(&req);
470  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
472 
475 
476  fuse_ino_t parent_fuse = parent;
477  parent = catalog_mgr->MangleInode(parent);
479  "cvmfs_lookup in parent inode: %" PRIu64 " for name: %s",
480  uint64_t(parent), name);
481 
482  PathString path;
483  PathString parent_path;
484  uint64_t live_inode = 0;
486  struct fuse_entry_param result;
487 
488  memset(&result, 0, sizeof(result));
489  double timeout = GetKcacheTimeout();
490  result.attr_timeout = timeout;
491  result.entry_timeout = timeout;
492 
493  // Special NFS lookups: . and ..
494  if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
495  if (GetDirentForInode(parent, &dirent)) {
496  if (strcmp(name, ".") == 0) {
497  goto lookup_reply_positive;
498  } else {
499  // Lookup for ".."
500  if (dirent.inode() == catalog_mgr->GetRootInode()) {
501  dirent.set_inode(1);
502  goto lookup_reply_positive;
503  }
504  if (!GetPathForInode(parent, &parent_path))
505  goto lookup_reply_negative;
506  if (GetDirentForPath(GetParentPath(parent_path), &dirent) > 0)
507  goto lookup_reply_positive;
508  }
509  }
510  // No entry for "." or no entry for ".."
511  if (dirent.GetSpecial() == catalog::kDirentNegative)
512  goto lookup_reply_negative;
513  else
514  goto lookup_reply_error;
515  assert(false);
516  }
517 
518  if (!GetPathForInode(parent, &parent_path)) {
519  LogCvmfs(kLogCvmfs, kLogDebug, "no path for parent inode found");
520  goto lookup_reply_negative;
521  }
522 
523  path.Assign(parent_path);
524  path.Append("/", 1);
525  path.Append(name, strlen(name));
526  mount_point_->tracer()->Trace(Tracer::kEventLookup, path, "lookup()");
527  live_inode = GetDirentForPath(path, &dirent);
528  if (live_inode == 0) {
529  if (dirent.GetSpecial() == catalog::kDirentNegative)
530  goto lookup_reply_negative;
531  else
532  goto lookup_reply_error;
533  }
534 
535  lookup_reply_positive:
536  if (!file_system_->IsNfsSource()) {
537  if (live_inode > 1) {
538  // live inode is stale (open file), we replace it
539  assert(dirent.IsRegular());
540  assert(dirent.inode() != live_inode);
541  // The new inode is put in the tracker with refcounter == 0
542  bool replaced = mount_point_->inode_tracker()->ReplaceInode(
543  live_inode, glue::InodeEx(dirent.inode(), dirent.mode()));
544  if (replaced)
546  }
548  glue::InodeEx(dirent.inode(), dirent.mode()), path);
549  }
550  // We do _not_ track (and evict) positive replies; among other things, test
551  // 076 fails with the following line uncommented
552  // mount_point_->dentry_tracker()->Add(parent_fuse, name, uint64_t(timeout));
554  result.ino = dirent.inode();
555  result.attr = dirent.GetStatStructure();
556  fuse_reply_entry(req, &result);
557  return;
558 
559  lookup_reply_negative:
560  // Will be a no-op if there is no fuse cache eviction
561  mount_point_->dentry_tracker()->Add(parent_fuse, name, uint64_t(timeout));
564  result.ino = 0;
565  fuse_reply_entry(req, &result);
566  return;
567 
568  lookup_reply_error:
570  fuse_reply_err(req, EIO);
571 }
572 
573 
577 static void cvmfs_forget(
578  fuse_req_t req,
579  fuse_ino_t ino,
580 #if CVMFS_USE_LIBFUSE == 2
581  unsigned long nlookup // NOLINT
582 #else
583  uint64_t nlookup
584 #endif
585 ) {
587 
589 
590  // The libfuse high-level library does the same
591  if (ino == FUSE_ROOT_ID) {
592  fuse_reply_none(req);
593  return;
594  }
595 
597  ino = mount_point_->catalog_mgr()->MangleInode(ino);
598  // This has been seen to deadlock on the debug log mutex on SL5. Problem of
599  // old kernel/fuse?
600 #if CVMFS_USE_LIBCVMFS == 2
601  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %u",
602  uint64_t(ino), nlookup);
603 #else
604  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
605  uint64_t(ino), nlookup);
606 #endif
607  if (!file_system_->IsNfsSource()) {
608  bool removed =
609  mount_point_->inode_tracker()->GetVfsPutRaii().VfsPut(ino, nlookup);
610  if (removed)
612  }
614  fuse_reply_none(req);
615 }
616 
617 
618 #if (FUSE_VERSION >= 29)
619 static void cvmfs_forget_multi(
620  fuse_req_t req,
621  size_t count,
622  struct fuse_forget_data *forgets
623 ) {
625 
627  if (file_system_->IsNfsSource()) {
628  fuse_reply_none(req);
629  return;
630  }
631 
633  {
634  glue::InodeTracker::VfsPutRaii vfs_put_raii =
638  for (size_t i = 0; i < count; ++i) {
639  if (forgets[i].ino == FUSE_ROOT_ID) {
640  continue;
641  }
642 
643  uint64_t ino = mount_point_->catalog_mgr()->MangleInode(forgets[i].ino);
644  LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %" PRIu64,
645  ino, forgets[i].nlookup);
646 
647  bool removed = vfs_put_raii.VfsPut(ino, forgets[i].nlookup);
648  if (removed)
649  evict_raii.Evict(ino);
650  }
651  }
653 
654  fuse_reply_none(req);
655 }
656 #endif // FUSE_VERSION >= 29
657 
658 
665 static void ReplyNegative(const catalog::DirectoryEntry &dirent,
666  fuse_req_t req)
667 {
668  if (dirent.GetSpecial() == catalog::kDirentNegative)
669  fuse_reply_err(req, ENOENT);
670  else
671  fuse_reply_err(req, EIO);
672 }
673 
674 
678 static void cvmfs_getattr(fuse_req_t req, fuse_ino_t ino,
679  struct fuse_file_info *fi)
680 {
682 
684  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
685  FuseInterruptCue ic(&req);
686  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
688 
690  ino = mount_point_->catalog_mgr()->MangleInode(ino);
691  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for inode: %" PRIu64,
692  uint64_t(ino));
693 
694  if (!CheckVoms(*fuse_ctx)) {
696  fuse_reply_err(req, EACCES);
697  return;
698  }
700  bool found = GetDirentForInode(ino, &dirent);
701  TraceInode(Tracer::kEventGetAttr, ino, "getattr()");
702  if ((!found && (dirent.inode() == ino)) || MayBeInPageCacheTracker(dirent)) {
703  // Serve retired inode from page cache tracker; even if we find it in the
704  // catalog, we replace the dirent by the page cache tracker version to
705  // not confuse open file handles
706  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr %" PRIu64 " "
707  "served from page cache tracker", ino);
708  shash::Any hash;
709  struct stat info;
710  bool is_open =
711  mount_point_->page_cache_tracker()->GetInfoIfOpen(ino, &hash, &info);
712  if (is_open) {
714  if (found && HasDifferentContent(dirent, hash, info)) {
715  // We should from now on provide the new inode information instead
716  // of the stale one. To this end, we need to invalidate the dentry to
717  // trigger a fresh LOOKUP call
718  uint64_t parent_ino;
719  NameString name;
721  dirent.inode(), &parent_ino, &name))
722  {
723  fuse_remounter_->InvalidateDentry(parent_ino, name);
724  }
726  }
727  fuse_reply_attr(req, &info, GetKcacheTimeout());
728  return;
729  }
730  }
732 
733  if (!found) {
734  ReplyNegative(dirent, req);
735  return;
736  }
737 
738  struct stat info = dirent.GetStatStructure();
739 
740  fuse_reply_attr(req, &info, GetKcacheTimeout());
741 }
742 
743 
747 static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino) {
749 
751  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
752  FuseInterruptCue ic(&req);
753  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
754 
756  ino = mount_point_->catalog_mgr()->MangleInode(ino);
757  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_readlink on inode: %" PRIu64,
758  uint64_t(ino));
759 
761  const bool found = GetDirentForInode(ino, &dirent);
762  TraceInode(Tracer::kEventReadlink, ino, "readlink()");
764 
765  if (!found) {
766  ReplyNegative(dirent, req);
767  return;
768  }
769 
770  if (!dirent.IsLink()) {
771  fuse_reply_err(req, EINVAL);
772  return;
773  }
774 
775  fuse_reply_readlink(req, dirent.symlink().c_str());
776 }
777 
778 
779 static void AddToDirListing(const fuse_req_t req,
780  const char *name, const struct stat *stat_info,
781  BigVector<char> *listing)
782 {
783  LogCvmfs(kLogCvmfs, kLogDebug, "Add to listing: %s, inode %" PRIu64,
784  name, uint64_t(stat_info->st_ino));
785  size_t remaining_size = listing->capacity() - listing->size();
786  const size_t entry_size = fuse_add_direntry(req, NULL, 0, name, stat_info, 0);
787 
788  while (entry_size > remaining_size) {
789  listing->DoubleCapacity();
790  remaining_size = listing->capacity() - listing->size();
791  }
792 
793  char *buffer;
794  bool large_alloc;
795  listing->ShareBuffer(&buffer, &large_alloc);
796  fuse_add_direntry(req, buffer + listing->size(),
797  remaining_size, name, stat_info,
798  listing->size() + entry_size);
799  listing->SetSize(listing->size() + entry_size);
800 }
801 
802 
806 static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino,
807  struct fuse_file_info *fi)
808 {
810 
811  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
812  FuseInterruptCue ic(&req);
813  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
815 
818  ino = catalog_mgr->MangleInode(ino);
819  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64,
820  uint64_t(ino));
821  if (!CheckVoms(*fuse_ctx)) {
823  fuse_reply_err(req, EACCES);
824  return;
825  }
826 
827  TraceInode(Tracer::kEventOpenDir, ino, "opendir()");
828  PathString path;
830  bool found = GetPathForInode(ino, &path);
831  if (!found) {
833  fuse_reply_err(req, ENOENT);
834  return;
835  }
836  found = GetDirentForInode(ino, &d);
837 
838  if (!found) {
840  ReplyNegative(d, req);
841  return;
842  }
843  if (!d.IsDirectory()) {
845  fuse_reply_err(req, ENOTDIR);
846  return;
847  }
848 
849  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64 ", path %s",
850  uint64_t(ino), path.c_str());
851 
852  // Build listing
853  BigVector<char> fuse_listing(512);
854 
855  // Add current directory link
856  struct stat info;
857  info = d.GetStatStructure();
858  AddToDirListing(req, ".", &info, &fuse_listing);
859 
860  // Add parent directory link
862  if (d.inode() != catalog_mgr->GetRootInode() &&
863  (GetDirentForPath(GetParentPath(path), &p) > 0))
864  {
865  info = p.GetStatStructure();
866  AddToDirListing(req, "..", &info, &fuse_listing);
867  }
868 
869  // Add all names
870  catalog::StatEntryList listing_from_catalog;
871  bool retval = catalog_mgr->ListingStat(path, &listing_from_catalog);
872 
873  if (!retval) {
875  fuse_listing.Clear(); // Buffer is shared, empty manually
876  fuse_reply_err(req, EIO);
877  return;
878  }
879  for (unsigned i = 0; i < listing_from_catalog.size(); ++i) {
880  // Fix inodes
881  PathString entry_path;
882  entry_path.Assign(path);
883  entry_path.Append("/", 1);
884  entry_path.Append(listing_from_catalog.AtPtr(i)->name.GetChars(),
885  listing_from_catalog.AtPtr(i)->name.GetLength());
886 
887  catalog::DirectoryEntry entry_dirent;
888  if (!GetDirentForPath(entry_path, &entry_dirent)) {
889  LogCvmfs(kLogCvmfs, kLogDebug, "listing entry %s vanished, skipping",
890  entry_path.c_str());
891  continue;
892  }
893 
894  struct stat fixed_info = listing_from_catalog.AtPtr(i)->info;
895  fixed_info.st_ino = entry_dirent.inode();
896  AddToDirListing(req, listing_from_catalog.AtPtr(i)->name.c_str(),
897  &fixed_info, &fuse_listing);
898  }
900 
901  DirectoryListing stream_listing;
902  stream_listing.size = fuse_listing.size();
903  stream_listing.capacity = fuse_listing.capacity();
904  bool large_alloc;
905  fuse_listing.ShareBuffer(&stream_listing.buffer, &large_alloc);
906  if (large_alloc)
907  stream_listing.capacity = 0;
908 
909  // Save the directory listing and return a handle to the listing
910  {
913  "linking directory handle %d to dir inode: %" PRIu64,
914  next_directory_handle_, uint64_t(ino));
915  (*directory_handles_)[next_directory_handle_] = stream_listing;
916  fi->fh = next_directory_handle_;
918  }
921 
922 #if (FUSE_VERSION >= 30)
923 #ifdef CVMFS_ENABLE_FUSE3_CACHE_READDIR
924  // This affects only reads on the same open directory handle (e.g. multiple
925  // reads with rewinddir() between them). A new opendir on the same directory
926  // will trigger readdir calls independently of this setting.
927  fi->cache_readdir = 1;
928 #endif
929 #endif
930  fuse_reply_open(req, fi);
931 }
932 
933 
937 static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino,
938  struct fuse_file_info *fi)
939 {
941 
942  ino = mount_point_->catalog_mgr()->MangleInode(ino);
943  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_releasedir on inode %" PRIu64
944  ", handle %d", uint64_t(ino), fi->fh);
945 
946  int reply = 0;
947 
948  {
950  DirectoryHandles::iterator iter_handle = directory_handles_->find(fi->fh);
951  if (iter_handle != directory_handles_->end()) {
952  if (iter_handle->second.capacity == 0)
953  smunmap(iter_handle->second.buffer);
954  else
955  free(iter_handle->second.buffer);
956  directory_handles_->erase(iter_handle);
958  } else {
959  reply = EINVAL;
960  }
961  }
962 
963  fuse_reply_err(req, reply);
964 }
965 
966 
970 static void ReplyBufferSlice(const fuse_req_t req, const char *buffer,
971  const size_t buffer_size, const off_t offset,
972  const size_t max_size)
973 {
974  if (offset < static_cast<int>(buffer_size)) {
975  fuse_reply_buf(req, buffer + offset,
976  std::min(static_cast<size_t>(buffer_size - offset), max_size));
977  } else {
978  fuse_reply_buf(req, NULL, 0);
979  }
980 }
981 
982 
986 static void cvmfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
987  off_t off, struct fuse_file_info *fi)
988 {
990 
992  "cvmfs_readdir on inode %" PRIu64 " reading %d bytes from offset %d",
993  uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)), size, off);
994 
995  DirectoryListing listing;
996 
998  DirectoryHandles::const_iterator iter_handle =
999  directory_handles_->find(fi->fh);
1000  if (iter_handle != directory_handles_->end()) {
1001  listing = iter_handle->second;
1002 
1003  ReplyBufferSlice(req, listing.buffer, listing.size, off, size);
1004  return;
1005  }
1006 
1007  fuse_reply_err(req, EINVAL);
1008 }
1009 
1011  struct fuse_file_info *fi)
1012 {
1014  fi->keep_cache = od.keep_cache;
1015  fi->direct_io = od.direct_io;
1016  if (fi->direct_io)
1018 }
1019 
1020 
1027 static void cvmfs_open(fuse_req_t req, fuse_ino_t ino,
1028  struct fuse_file_info *fi)
1029 {
1031 
1032  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1033  FuseInterruptCue ic(&req);
1034  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1035  fuse_remounter_->fence()->Enter();
1037  ino = catalog_mgr->MangleInode(ino);
1038  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_open on inode: %" PRIu64,
1039  uint64_t(ino));
1040 
1041  int fd = -1;
1042  catalog::DirectoryEntry dirent;
1043  PathString path;
1044 
1045  bool found = GetPathForInode(ino, &path);
1046  if (!found) {
1047  fuse_remounter_->fence()->Leave();
1048  fuse_reply_err(req, ENOENT);
1049  return;
1050  }
1051  found = GetDirentForInode(ino, &dirent);
1052  if (!found) {
1053  fuse_remounter_->fence()->Leave();
1054  ReplyNegative(dirent, req);
1055  return;
1056  }
1057 
1058  if (!CheckVoms(*fuse_ctx)) {
1059  fuse_remounter_->fence()->Leave();
1060  fuse_reply_err(req, EACCES);
1061  return;
1062  }
1063 
1064  mount_point_->tracer()->Trace(Tracer::kEventOpen, path, "open()");
1065  // Don't check. Either done by the OS or one wants to purposefully work
1066  // around wrong open flags
1067  // if ((fi->flags & 3) != O_RDONLY) {
1068  // fuse_reply_err(req, EROFS);
1069  // return;
1070  // }
1071 #ifdef __APPLE__
1072  if ((fi->flags & O_SHLOCK) || (fi->flags & O_EXLOCK)) {
1073  fuse_remounter_->fence()->Leave();
1074  fuse_reply_err(req, EOPNOTSUPP);
1075  return;
1076  }
1077 #endif
1078  if (fi->flags & O_EXCL) {
1079  fuse_remounter_->fence()->Leave();
1080  fuse_reply_err(req, EEXIST);
1081  return;
1082  }
1083 
1084  perf::Inc(file_system_->n_fs_open()); // Count actual open / fetch operations
1085 
1087  if (!dirent.IsChunkedFile()) {
1088  if (dirent.IsDirectIo()) {
1089  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1090  } else {
1091  open_directives =
1093  ino, dirent.checksum(), dirent.GetStatStructure());
1094  }
1095  fuse_remounter_->fence()->Leave();
1096  } else {
1098  "chunked file %s opened (download delayed to read() call)",
1099  path.c_str());
1100 
1101  if (perf::Xadd(file_system_->no_open_files(), 1) >=
1102  (static_cast<int>(max_open_files_))-kNumReservedFd)
1103  {
1105  fuse_remounter_->fence()->Leave();
1106  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1107  fuse_reply_err(req, EMFILE);
1108  return;
1109  }
1110 
1111  // Figure out unique inode from annotated catalog
1112  // TODO(jblomer): we only need to lookup if the inode is not from the
1113  // current generation
1114  catalog::DirectoryEntry dirent_origin;
1115  if (!catalog_mgr->LookupPath(path, catalog::kLookupSole, &dirent_origin)) {
1116  fuse_remounter_->fence()->Leave();
1118  "chunked file %s vanished unexpectedly", path.c_str());
1119  fuse_reply_err(req, ENOENT);
1120  return;
1121  }
1122  const uint64_t unique_inode = dirent_origin.inode();
1123 
1124  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1125  chunk_tables->Lock();
1126  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1127  chunk_tables->Unlock();
1128 
1129  // Retrieve File chunks from the catalog
1131  if (!catalog_mgr->ListFileChunks(path, dirent.hash_algorithm(),
1132  chunks.weak_ref()) ||
1133  chunks->IsEmpty())
1134  {
1135  fuse_remounter_->fence()->Leave();
1136  LogCvmfs(kLogCvmfs, kLogDebug| kLogSyslogErr, "file %s is marked as "
1137  "'chunked', but no chunks found.", path.c_str());
1138  fuse_reply_err(req, EIO);
1139  return;
1140  }
1141  fuse_remounter_->fence()->Leave();
1142 
1143  chunk_tables->Lock();
1144  // Check again to avoid race
1145  if (!chunk_tables->inode2chunks.Contains(unique_inode)) {
1146  chunk_tables->inode2chunks.Insert(
1147  unique_inode, FileChunkReflist(chunks.Release(), path,
1148  dirent.compression_algorithm(),
1149  dirent.IsExternalFile()));
1150  chunk_tables->inode2references.Insert(unique_inode, 1);
1151  } else {
1152  uint32_t refctr;
1153  bool retval =
1154  chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1155  assert(retval);
1156  chunk_tables->inode2references.Insert(unique_inode, refctr+1);
1157  }
1158  } else {
1159  fuse_remounter_->fence()->Leave();
1160  uint32_t refctr;
1161  bool retval =
1162  chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1163  assert(retval);
1164  chunk_tables->inode2references.Insert(unique_inode, refctr+1);
1165  }
1166 
1167  // Update the chunk handle list
1169  "linking chunk handle %d to unique inode: %" PRIu64,
1170  chunk_tables->next_handle, uint64_t(unique_inode));
1171  chunk_tables->handle2fd.Insert(chunk_tables->next_handle, ChunkFd());
1172  chunk_tables->handle2uniqino.Insert(chunk_tables->next_handle,
1173  unique_inode);
1174 
1175  // Generate artificial content hash as hash over chunk hashes
1176  // TODO(jblomer): we may want to cache the result in the chunk tables
1177  FileChunkReflist chunk_reflist;
1178  bool retval =
1179  chunk_tables->inode2chunks.Lookup(unique_inode, &chunk_reflist);
1180  assert(retval);
1181 
1182  fi->fh = chunk_tables->next_handle;
1183  if (dirent.IsDirectIo()) {
1184  open_directives = mount_point_->page_cache_tracker()->OpenDirect();
1185  } else {
1186  open_directives = mount_point_->page_cache_tracker()->Open(
1187  ino, chunk_reflist.HashChunkList(), dirent.GetStatStructure());
1188  }
1189  FillOpenFlags(open_directives, fi);
1190  fi->fh = static_cast<uint64_t>(-static_cast<int64_t>(fi->fh));
1191  ++chunk_tables->next_handle;
1192  chunk_tables->Unlock();
1193 
1194  fuse_reply_open(req, fi);
1195  return;
1196  }
1197 
1198  Fetcher *this_fetcher = dirent.IsExternalFile()
1200  : mount_point_->fetcher();
1201  fd = this_fetcher->Fetch(
1202  dirent.checksum(),
1203  dirent.size(),
1204  string(path.GetChars(), path.GetLength()),
1205  dirent.compression_algorithm(),
1209 
1210  if (fd >= 0) {
1212  (static_cast<int>(max_open_files_))-kNumReservedFd) {
1213  LogCvmfs(kLogCvmfs, kLogDebug, "file %s opened (fd %d)",
1214  path.c_str(), fd);
1215  fi->fh = fd;
1216  FillOpenFlags(open_directives, fi);
1217  fuse_reply_open(req, fi);
1218  return;
1219  } else {
1220  if (file_system_->cache_mgr()->Close(fd) == 0)
1222  LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded");
1223  fuse_reply_err(req, EMFILE);
1224  return;
1225  }
1226  assert(false);
1227  }
1228 
1229  // fd < 0
1231  "failed to open inode: %" PRIu64 ", CAS key %s, error code %d",
1232  uint64_t(ino), dirent.checksum().ToString().c_str(), errno);
1233  if (errno == EMFILE) {
1234  fuse_reply_err(req, EMFILE);
1235  return;
1236  }
1237 
1239 
1241  fuse_reply_err(req, -fd);
1242 }
1243 
1244 
1248 static void cvmfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
1249  struct fuse_file_info *fi)
1250 {
1252 
1254  "cvmfs_read inode: %" PRIu64 " reading %d bytes from offset %d "
1255  "fd %d", uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)),
1256  size, off, fi->fh);
1258 
1259  // Get data chunk (<=128k guaranteed by Fuse)
1260  char *data = static_cast<char *>(alloca(size));
1261  unsigned int overall_bytes_fetched = 0;
1262 
1263  int64_t fd = static_cast<int64_t>(fi->fh);
1264  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1266 
1267  // Do we have a a chunked file?
1268  if (fd < 0) {
1269  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1270  FuseInterruptCue ic(&req);
1271  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1272 
1273  const uint64_t chunk_handle = abs_fd;
1274  uint64_t unique_inode;
1275  ChunkFd chunk_fd;
1276  FileChunkReflist chunks;
1277  bool retval;
1278 
1279  // Fetch unique inode, chunk list and file descriptor
1280  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1281  chunk_tables->Lock();
1282  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1283  if (!retval) {
1284  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1285  unique_inode = ino;
1286  }
1287  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &chunks);
1288  assert(retval);
1289  chunk_tables->Unlock();
1290 
1291  unsigned chunk_idx = chunks.FindChunkIdx(off);
1292 
1293  // Lock chunk handle
1294  pthread_mutex_t *handle_lock = chunk_tables->Handle2Lock(chunk_handle);
1295  MutexLockGuard m(handle_lock);
1296  chunk_tables->Lock();
1297  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1298  assert(retval);
1299  chunk_tables->Unlock();
1300 
1301  // Fetch all needed chunks and read the requested data
1302  off_t offset_in_chunk = off - chunks.list->AtPtr(chunk_idx)->offset();
1303  do {
1304  // Open file descriptor to chunk
1305  if ((chunk_fd.fd == -1) || (chunk_fd.chunk_idx != chunk_idx)) {
1306  if (chunk_fd.fd != -1) file_system_->cache_mgr()->Close(chunk_fd.fd);
1307  string verbose_path = "Part of " + chunks.path.ToString();
1308  if (chunks.external_data) {
1309  chunk_fd.fd = mount_point_->external_fetcher()->Fetch(
1310  chunks.list->AtPtr(chunk_idx)->content_hash(),
1311  chunks.list->AtPtr(chunk_idx)->size(),
1312  verbose_path,
1313  chunks.compression_alg,
1317  chunks.path.ToString(),
1318  chunks.list->AtPtr(chunk_idx)->offset());
1319  } else {
1320  chunk_fd.fd = mount_point_->fetcher()->Fetch(
1321  chunks.list->AtPtr(chunk_idx)->content_hash(),
1322  chunks.list->AtPtr(chunk_idx)->size(),
1323  verbose_path,
1324  chunks.compression_alg,
1328  }
1329  if (chunk_fd.fd < 0) {
1330  chunk_fd.fd = -1;
1331  chunk_tables->Lock();
1332  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1333  chunk_tables->Unlock();
1334  fuse_reply_err(req, EIO);
1335  return;
1336  }
1337  chunk_fd.chunk_idx = chunk_idx;
1338  }
1339 
1340  LogCvmfs(kLogCvmfs, kLogDebug, "reading from chunk fd %d",
1341  chunk_fd.fd);
1342  // Read data from chunk
1343  const size_t bytes_to_read = size - overall_bytes_fetched;
1344  const size_t remaining_bytes_in_chunk =
1345  chunks.list->AtPtr(chunk_idx)->size() - offset_in_chunk;
1346  size_t bytes_to_read_in_chunk =
1347  std::min(bytes_to_read, remaining_bytes_in_chunk);
1348  const int64_t bytes_fetched = file_system_->cache_mgr()->Pread(
1349  chunk_fd.fd,
1350  data + overall_bytes_fetched,
1351  bytes_to_read_in_chunk,
1352  offset_in_chunk);
1353 
1354  if (bytes_fetched < 0) {
1355  LogCvmfs(kLogCvmfs, kLogSyslogErr, "read err no %" PRId64 " (%s)",
1356  bytes_fetched, chunks.path.ToString().c_str());
1357  chunk_tables->Lock();
1358  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1359  chunk_tables->Unlock();
1360  fuse_reply_err(req, -bytes_fetched);
1361  return;
1362  }
1363  overall_bytes_fetched += bytes_fetched;
1364 
1365  // Proceed to the next chunk to keep on reading data
1366  ++chunk_idx;
1367  offset_in_chunk = 0;
1368  } while ((overall_bytes_fetched < size) &&
1369  (chunk_idx < chunks.list->size()));
1370 
1371  // Update chunk file descriptor
1372  chunk_tables->Lock();
1373  chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd);
1374  chunk_tables->Unlock();
1375  LogCvmfs(kLogCvmfs, kLogDebug, "released chunk file descriptor %d",
1376  chunk_fd.fd);
1377  } else {
1378  int64_t nbytes = file_system_->cache_mgr()->Pread(abs_fd, data, size, off);
1379  if (nbytes < 0) {
1380  fuse_reply_err(req, -nbytes);
1381  return;
1382  }
1383  overall_bytes_fetched = nbytes;
1384  }
1385 
1386  // Push it to user
1387  fuse_reply_buf(req, data, overall_bytes_fetched);
1388  LogCvmfs(kLogCvmfs, kLogDebug, "pushed %d bytes to user",
1389  overall_bytes_fetched);
1390 }
1391 
1392 
1396 static void cvmfs_release(fuse_req_t req, fuse_ino_t ino,
1397  struct fuse_file_info *fi)
1398 {
1400 
1401  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1402  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_release on inode: %" PRIu64,
1403  uint64_t(ino));
1404  int64_t fd = static_cast<int64_t>(fi->fh);
1405  uint64_t abs_fd = (fd < 0) ? -fd : fd;
1408  }
1410 
1411  // do we have a chunked file?
1412  if (fd < 0) {
1413  const uint64_t chunk_handle = abs_fd;
1414  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk handle %" PRIu64,
1415  chunk_handle);
1416  uint64_t unique_inode;
1417  ChunkFd chunk_fd;
1418  FileChunkReflist chunks;
1419  uint32_t refctr;
1420  bool retval;
1421 
1422  ChunkTables *chunk_tables = mount_point_->chunk_tables();
1423  chunk_tables->Lock();
1424  retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode);
1425  if (!retval) {
1426  LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino");
1427  unique_inode = ino;
1428  } else {
1429  chunk_tables->handle2uniqino.Erase(chunk_handle);
1430  }
1431  retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd);
1432  assert(retval);
1433  chunk_tables->handle2fd.Erase(chunk_handle);
1434 
1435  retval = chunk_tables->inode2references.Lookup(unique_inode, &refctr);
1436  assert(retval);
1437  refctr--;
1438  if (refctr == 0) {
1439  LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk list for inode %" PRIu64,
1440  uint64_t(unique_inode));
1441  FileChunkReflist to_delete;
1442  retval = chunk_tables->inode2chunks.Lookup(unique_inode, &to_delete);
1443  assert(retval);
1444  chunk_tables->inode2references.Erase(unique_inode);
1445  chunk_tables->inode2chunks.Erase(unique_inode);
1446  delete to_delete.list;
1447  } else {
1448  chunk_tables->inode2references.Insert(unique_inode, refctr);
1449  }
1450  chunk_tables->Unlock();
1451 
1452  if (chunk_fd.fd != -1)
1453  file_system_->cache_mgr()->Close(chunk_fd.fd);
1455  } else {
1456  if (file_system_->cache_mgr()->Close(abs_fd) == 0) {
1458  }
1459  }
1460  fuse_reply_err(req, 0);
1461 }
1462 
1463 
1464 static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino) {
1465  ino = mount_point_->catalog_mgr()->MangleInode(ino);
1466  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_statfs on inode: %" PRIu64,
1467  uint64_t(ino));
1468 
1469  // If we return 0 it will cause the fs to be ignored in "df"
1470  struct statvfs info;
1471  memset(&info, 0, sizeof(info));
1472 
1473  TraceInode(Tracer::kEventStatFs, ino, "statfs()");
1474 
1475  // Unmanaged cache
1478  {
1479  fuse_reply_statfs(req, &info);
1480  return;
1481  }
1482 
1483  uint64_t available = 0;
1484  uint64_t size = file_system_->cache_mgr()->quota_mgr()->GetSize();
1485  uint64_t capacity = file_system_->cache_mgr()->quota_mgr()->GetCapacity();
1486  // Fuse/OS X doesn't like values < 512
1487  info.f_bsize = info.f_frsize = 512;
1488 
1489  if (capacity == (uint64_t)(-1)) {
1490  // Unknown capacity, set capacity = size
1491  info.f_blocks = size / info.f_bsize;
1492  } else {
1493  // Take values from LRU module
1494  info.f_blocks = capacity / info.f_bsize;
1495  available = capacity - size;
1496  }
1497 
1498  info.f_bfree = info.f_bavail = available / info.f_bsize;
1499 
1500  // Inodes / entries
1501  fuse_remounter_->fence()->Enter();
1502  uint64_t all_inodes = mount_point_->catalog_mgr()->all_inodes();
1503  uint64_t loaded_inode = mount_point_->catalog_mgr()->loaded_inodes();
1504  info.f_files = all_inodes;
1505  info.f_ffree = info.f_favail = all_inodes - loaded_inode;
1506  fuse_remounter_->fence()->Leave();
1507 
1508  fuse_reply_statfs(req, &info);
1509 }
1510 
1511 #ifdef __APPLE__
1512 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1513  size_t size, uint32_t position)
1514 #else
1515 static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
1516  size_t size)
1517 #endif
1518 {
1519  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1520  FuseInterruptCue ic(&req);
1521  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1522 
1523  fuse_remounter_->fence()->Enter();
1525  ino = catalog_mgr->MangleInode(ino);
1527  "cvmfs_getxattr on inode: %" PRIu64 " for xattr: %s",
1528  uint64_t(ino), name);
1529  if (!CheckVoms(*fuse_ctx)) {
1530  fuse_remounter_->fence()->Leave();
1531  fuse_reply_err(req, EACCES);
1532  return;
1533  }
1534  TraceInode(Tracer::kEventGetXAttr, ino, "getxattr()");
1535 
1536  const string attr = name;
1538  const bool found = GetDirentForInode(ino, &d);
1539  bool retval;
1540  XattrList xattrs;
1541 
1542  PathString path;
1543  retval = GetPathForInode(ino, &path);
1544  assert(retval);
1545  if (d.IsLink()) {
1546  catalog::LookupOptions lookup_options = static_cast<catalog::LookupOptions>(
1548  catalog::DirectoryEntry raw_symlink;
1549  retval = catalog_mgr->LookupPath(path, lookup_options, &raw_symlink);
1550  assert(retval);
1551  d.set_symlink(raw_symlink.symlink());
1552  }
1553  if (d.HasXattrs()) {
1554  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1555  assert(retval);
1556  }
1557 
1558  bool magic_xattr_success = true;
1560  attr, path, &d));
1561  if (!magic_xattr.IsNull()) {
1562  magic_xattr_success = magic_xattr->PrepareValueFenced();
1563  }
1564 
1565  fuse_remounter_->fence()->Leave();
1566 
1567  if (!found) {
1568  ReplyNegative(d, req);
1569  return;
1570  }
1571 
1572  if (!magic_xattr_success) {
1573  fuse_reply_err(req, ENOATTR);
1574  return;
1575  }
1576 
1577  string attribute_value;
1578 
1579  if (!magic_xattr.IsNull()) {
1580  attribute_value = magic_xattr->GetValue();
1581  } else {
1582  if (!xattrs.Get(attr, &attribute_value)) {
1583  fuse_reply_err(req, ENOATTR);
1584  return;
1585  }
1586  }
1587 
1588  if (size == 0) {
1589  fuse_reply_xattr(req, attribute_value.length());
1590  } else if (size >= attribute_value.length()) {
1591  fuse_reply_buf(req, &attribute_value[0], attribute_value.length());
1592  } else {
1593  fuse_reply_err(req, ERANGE);
1594  }
1595 }
1596 
1597 
1598 static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
1599  const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req);
1600  FuseInterruptCue ic(&req);
1601  ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid, &ic);
1602 
1603  fuse_remounter_->fence()->Enter();
1605  ino = catalog_mgr->MangleInode(ino);
1606  TraceInode(Tracer::kEventListAttr, ino, "listxattr()");
1608  "cvmfs_listxattr on inode: %" PRIu64 ", size %u [visibility %d]",
1609  uint64_t(ino), size,
1611 
1613  const bool found = GetDirentForInode(ino, &d);
1614  XattrList xattrs;
1615  if (d.HasXattrs()) {
1616  PathString path;
1617  bool retval = GetPathForInode(ino, &path);
1618  assert(retval);
1619  retval = catalog_mgr->LookupXattrs(path, &xattrs);
1620  assert(retval);
1621  }
1622  fuse_remounter_->fence()->Leave();
1623 
1624  if (!found) {
1625  ReplyNegative(d, req);
1626  return;
1627  }
1628 
1629  string attribute_list;
1630  attribute_list = mount_point_->magic_xattr_mgr()->GetListString(&d);
1631  attribute_list = xattrs.ListKeysPosix(attribute_list);
1632 
1633  if (size == 0) {
1634  fuse_reply_xattr(req, attribute_list.length());
1635  } else if (size >= attribute_list.length()) {
1636  if (attribute_list.empty())
1637  fuse_reply_buf(req, NULL, 0);
1638  else
1639  fuse_reply_buf(req, &attribute_list[0], attribute_list.length());
1640  } else {
1641  fuse_reply_err(req, ERANGE);
1642  }
1643 }
1644 
1645 
1646 bool Evict(const string &path) {
1647  catalog::DirectoryEntry dirent;
1648  fuse_remounter_->fence()->Enter();
1649  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1650  fuse_remounter_->fence()->Leave();
1651 
1652  if (!found || !dirent.IsRegular())
1653  return false;
1654  file_system_->cache_mgr()->quota_mgr()->Remove(dirent.checksum());
1655  return true;
1656 }
1657 
1658 
1659 bool Pin(const string &path) {
1660  catalog::DirectoryEntry dirent;
1661  fuse_remounter_->fence()->Enter();
1662  const bool found = (GetDirentForPath(PathString(path), &dirent) > 0);
1663  if (!found || !dirent.IsRegular()) {
1664  fuse_remounter_->fence()->Leave();
1665  return false;
1666  }
1667 
1668  if (!dirent.IsChunkedFile()) {
1669  fuse_remounter_->fence()->Leave();
1670  } else {
1671  FileChunkList chunks;
1673  PathString(path), dirent.hash_algorithm(), &chunks);
1674  fuse_remounter_->fence()->Leave();
1675  for (unsigned i = 0; i < chunks.size(); ++i) {
1676  bool retval =
1678  chunks.AtPtr(i)->content_hash(),
1679  chunks.AtPtr(i)->size(),
1680  "Part of " + path,
1681  false);
1682  if (!retval)
1683  return false;
1684  int fd = -1;
1685  if (dirent.IsExternalFile()) {
1687  chunks.AtPtr(i)->content_hash(),
1688  chunks.AtPtr(i)->size(),
1689  "Part of " + path,
1690  dirent.compression_algorithm(),
1692  path,
1693  chunks.AtPtr(i)->offset());
1694  } else {
1695  fd = mount_point_->fetcher()->Fetch(
1696  chunks.AtPtr(i)->content_hash(),
1697  chunks.AtPtr(i)->size(),
1698  "Part of " + path,
1699  dirent.compression_algorithm(),
1701  }
1702  if (fd < 0) {
1703  return false;
1704  }
1705  file_system_->cache_mgr()->Close(fd);
1706  }
1707  return true;
1708  }
1709 
1710  bool retval = file_system_->cache_mgr()->quota_mgr()->Pin(
1711  dirent.checksum(), dirent.size(), path, false);
1712  if (!retval)
1713  return false;
1714  Fetcher *this_fetcher = dirent.IsExternalFile()
1716  : mount_point_->fetcher();
1717  int fd = this_fetcher->Fetch(
1718  dirent.checksum(), dirent.size(), path, dirent.compression_algorithm(),
1720  if (fd < 0) {
1721  return false;
1722  }
1723  file_system_->cache_mgr()->Close(fd);
1724  return true;
1725 }
1726 
1727 
1731 static void cvmfs_init(void *userdata, struct fuse_conn_info *conn) {
1732  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_init");
1733 
1734  // NFS support
1735 #ifdef CVMFS_NFS_SUPPORT
1736  conn->want |= FUSE_CAP_EXPORT_SUPPORT;
1737 #endif
1738 
1739  if (mount_point_->enforce_acls()) {
1740 #ifdef FUSE_CAP_POSIX_ACL
1741  if ((conn->capable & FUSE_CAP_POSIX_ACL) == 0) {
1743  "ACL support requested but missing fuse kernel support, "
1744  "aborting");
1745  }
1746  conn->want |= FUSE_CAP_POSIX_ACL;
1747  LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, "enforcing ACLs");
1748 #else
1750  "ACL support requested but not available in this version of "
1751  "libfuse, aborting");
1752 #endif
1753  }
1754 }
1755 
1756 static void cvmfs_destroy(void *unused __attribute__((unused))) {
1757  // The debug log is already closed at this point
1758  LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_destroy");
1759 }
1760 
1764 static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations) {
1765  memset(cvmfs_operations, 0, sizeof(*cvmfs_operations));
1766 
1767  // Init/Fini
1768  cvmfs_operations->init = cvmfs_init;
1769  cvmfs_operations->destroy = cvmfs_destroy;
1770 
1771  cvmfs_operations->lookup = cvmfs_lookup;
1772  cvmfs_operations->getattr = cvmfs_getattr;
1773  cvmfs_operations->readlink = cvmfs_readlink;
1774  cvmfs_operations->open = cvmfs_open;
1775  cvmfs_operations->read = cvmfs_read;
1776  cvmfs_operations->release = cvmfs_release;
1777  cvmfs_operations->opendir = cvmfs_opendir;
1778  cvmfs_operations->readdir = cvmfs_readdir;
1779  cvmfs_operations->releasedir = cvmfs_releasedir;
1780  cvmfs_operations->statfs = cvmfs_statfs;
1781  cvmfs_operations->getxattr = cvmfs_getxattr;
1782  cvmfs_operations->listxattr = cvmfs_listxattr;
1783  cvmfs_operations->forget = cvmfs_forget;
1784 #if (FUSE_VERSION >= 29)
1785  cvmfs_operations->forget_multi = cvmfs_forget_multi;
1786 #endif
1787 }
1788 
1789 // Called by cvmfs_talk when switching into read-only cache mode
1791  if (cvmfs::unpin_listener_) {
1793  cvmfs::unpin_listener_ = NULL;
1794  }
1798  }
1799 }
1800 
1801 bool SendFuseFd(const std::string &socket_path) {
1802  int fuse_fd;
1803 #if (FUSE_VERSION >= 30)
1804  fuse_fd = fuse_session_fd(*reinterpret_cast<struct fuse_session**>(
1806 #else
1807  fuse_fd = fuse_chan_fd(*reinterpret_cast<struct fuse_chan**>(
1809 #endif
1810  assert(fuse_fd >= 0);
1811  int sock_fd = ConnectSocket(socket_path);
1812  if (sock_fd < 0) {
1813  LogCvmfs(kLogCvmfs, kLogDebug, "cannot connect to socket %s: %d",
1814  socket_path.c_str(), errno);
1815  return false;
1816  }
1817  bool retval = SendFd2Socket(sock_fd, fuse_fd);
1818  close(sock_fd);
1819  return retval;
1820 }
1821 
1822 } // namespace cvmfs
1823 
1824 
1825 string *g_boot_error = NULL;
1826 
1827 __attribute__((visibility("default")))
1828 loader::CvmfsExports *g_cvmfs_exports = NULL;
1829 
1836 
1837  virtual bool PrepareValueFenced() {
1838  catalogs_valid_until_ = cvmfs::fuse_remounter_->catalogs_valid_until();
1839  return true;
1840  }
1841 
1842  virtual std::string GetValue() {
1843  if (catalogs_valid_until_ == MountPoint::kIndefiniteDeadline) {
1844  return "never (fixed root catalog)";
1845  } else {
1846  time_t now = time(NULL);
1847  return StringifyInt( (catalogs_valid_until_ - now) / 60);
1848  }
1849  }
1850 };
1851 
1853  virtual std::string GetValue() {
1854  return StringifyInt(
1855  cvmfs::inode_generation_info_.inode_generation +
1857  }
1858 };
1859 
1861  virtual std::string GetValue() {
1863  }
1864 };
1865 
1867  virtual std::string GetValue() { return StringifyInt(cvmfs::pid_); }
1868 };
1869 
1871  virtual std::string GetValue() {
1872  time_t now = time(NULL);
1873  uint64_t uptime = now - cvmfs::loader_exports_->boot_time;
1874  return StringifyInt(uptime / 60);
1875  }
1876 };
1877 
1882 static void RegisterMagicXattrs() {
1884  mgr->Register("user.expires", new ExpiresMagicXattr());
1885  mgr->Register("user.inode_max", new InodeMaxMagicXattr());
1886  mgr->Register("user.pid", new PidMagicXattr());
1887  mgr->Register("user.maxfd", new MaxFdMagicXattr());
1888  mgr->Register("user.uptime", new UptimeMagicXattr());
1889 }
1890 
1896  const string &mount_path,
1897  const string &fqrn,
1899 {
1900  fs_info.wait_workspace = false;
1901  FileSystem *file_system = FileSystem::Create(fs_info);
1902 
1903  if (file_system->boot_status() == loader::kFailLockWorkspace) {
1904  string fqrn_from_xattr;
1905  int retval = platform_getxattr(mount_path, "user.fqrn", &fqrn_from_xattr);
1906  if (!retval) {
1907  // Cvmfs not mounted anymore, but another cvmfs process is still in
1908  // shutdown procedure. Try again and wait for lock
1909  delete file_system;
1910  fs_info.wait_workspace = true;
1911  file_system = FileSystem::Create(fs_info);
1912  } else {
1913  if (fqrn_from_xattr == fqrn) {
1915  "repository already mounted on %s", mount_path.c_str());
1917  } else {
1919  "CernVM-FS repository %s already mounted on %s",
1920  fqrn.c_str(), mount_path.c_str());
1922  }
1923  }
1924  }
1925 
1926  return file_system;
1927 }
1928 
1929 
1930 static void InitOptionsMgr(const loader::LoaderExports *loader_exports) {
1931  if (loader_exports->version >= 3 && loader_exports->simple_options_parsing) {
1933  new DefaultOptionsTemplateManager(loader_exports->repository_name));
1934  } else {
1936  new DefaultOptionsTemplateManager(loader_exports->repository_name));
1937  }
1938 
1939  if (loader_exports->config_files != "") {
1940  vector<string> tokens = SplitString(loader_exports->config_files, ':');
1941  for (unsigned i = 0, s = tokens.size(); i < s; ++i) {
1942  cvmfs::options_mgr_->ParsePath(tokens[i], false);
1943  }
1944  } else {
1946  }
1947 }
1948 
1949 
1950 static int Init(const loader::LoaderExports *loader_exports) {
1951  g_boot_error = new string("unknown error");
1952  cvmfs::loader_exports_ = loader_exports;
1953  InitOptionsMgr(loader_exports);
1954 
1956  fs_info.type = FileSystem::kFsFuse;
1957  fs_info.name = loader_exports->repository_name;
1958  fs_info.exe_path = loader_exports->program_name;
1959  fs_info.options_mgr = cvmfs::options_mgr_;
1960  fs_info.foreground = loader_exports->foreground;
1962  loader_exports->mount_point,
1963  loader_exports->repository_name,
1964  fs_info);
1965  if (!cvmfs::file_system_->IsValid()) {
1967  return cvmfs::file_system_->boot_status();
1968  }
1969 
1972  if (!cvmfs::mount_point_->IsValid()) {
1974  return cvmfs::mount_point_->boot_status();
1975  }
1976 
1978 
1980  cvmfs::directory_handles_->set_empty_key((uint64_t)(-1));
1981  cvmfs::directory_handles_->set_deleted_key((uint64_t)(-2));
1982 
1983  LogCvmfs(kLogCvmfs, kLogDebug, "fuse inode size is %d bits",
1984  sizeof(fuse_ino_t) * 8);
1985 
1990  LogCvmfs(kLogCvmfs, kLogDebug, "root inode is %" PRIu64,
1991  uint64_t(cvmfs::mount_point_->catalog_mgr()->GetRootInode()));
1992 
1993  void **channel_or_session = NULL;
1994  if (loader_exports->version >= 4) {
1995  channel_or_session = loader_exports->fuse_channel_or_session;
1996  }
1997 
1998  bool fuse_notify_invalidation = true;
1999  std::string buf;
2000  if (cvmfs::options_mgr_->GetValue("CVMFS_FUSE_NOTIFY_INVALIDATION",
2001  &buf)) {
2002  if (!cvmfs::options_mgr_->IsOn(buf)) {
2003  fuse_notify_invalidation = false;
2005  }
2006  }
2009  channel_or_session, fuse_notify_invalidation);
2010 
2011  // Monitor, check for maximum number of open files
2012  if (cvmfs::UseWatchdog()) {
2013  cvmfs::watchdog_ = Watchdog::Create("./stacktrace." +
2014  loader_exports->repository_name);
2015  if (cvmfs::watchdog_ == NULL) {
2016  *g_boot_error = "failed to initialize watchdog.";
2017  return loader::kFailMonitor;
2018  }
2019  }
2021 
2022  // Control & command interface
2024  cvmfs::mount_point_->talk_socket_path(),
2027  if ((cvmfs::mount_point_->talk_socket_uid() != 0) ||
2028  (cvmfs::mount_point_->talk_socket_gid() != 0))
2029  {
2030  uid_t tgt_uid = cvmfs::mount_point_->talk_socket_uid();
2031  gid_t tgt_gid = cvmfs::mount_point_->talk_socket_gid();
2032  int rvi = chown(cvmfs::mount_point_->talk_socket_path().c_str(),
2033  tgt_uid, tgt_gid);
2034  if (rvi != 0) {
2035  *g_boot_error = std::string("failed to set talk socket ownership - ")
2036  + "target " + StringifyInt(tgt_uid) + ":" + StringifyInt(tgt_uid)
2037  + ", user " + StringifyInt(geteuid()) + ":" + StringifyInt(getegid());
2038  return loader::kFailTalk;
2039  }
2040  }
2041  if (cvmfs::talk_mgr_ == NULL) {
2042  *g_boot_error = "failed to initialize talk socket (" +
2043  StringifyInt(errno) + ")";
2044  return loader::kFailTalk;
2045  }
2046 
2047  // Notification system client
2048  {
2050  if (options->IsDefined("CVMFS_NOTIFICATION_SERVER")) {
2051  std::string config;
2052  options->GetValue("CVMFS_NOTIFICATION_SERVER", &config);
2053  const std::string repo_name = cvmfs::mount_point_->fqrn();
2055  new NotificationClient(config, repo_name, cvmfs::fuse_remounter_,
2056  cvmfs::mount_point_->download_mgr(),
2058  }
2059  }
2060 
2061 
2062  auto_umount::SetMountpoint(loader_exports->mount_point);
2063 
2064  return loader::kFailOk;
2065 }
2066 
2067 
2071 static void Spawn() {
2072  // First thing: fork off the watchdog while we still have a single-threaded
2073  // well-defined state
2074  cvmfs::pid_ = getpid();
2075  if (cvmfs::watchdog_) {
2078  }
2079 
2081  if (cvmfs::mount_point_->dentry_tracker()->is_active()) {
2083  cvmfs::mount_point_->kcache_timeout_sec()); // Usually every minute
2084  }
2085 
2088  if (cvmfs::mount_point_->resolv_conf_watcher() != NULL)
2091  quota_mgr->Spawn();
2092  if (quota_mgr->HasCapability(QuotaManager::kCapListeners)) {
2094  quota_mgr,
2095  cvmfs::mount_point_->uuid()->uuid() + "-watchdog");
2097  quota_mgr,
2098  cvmfs::mount_point_->catalog_mgr(),
2099  cvmfs::mount_point_->uuid()->uuid() + "-unpin");
2100  }
2103 
2104  if (cvmfs::notification_client_ != NULL) {
2106  }
2107 
2108  if (cvmfs::file_system_->nfs_maps() != NULL)
2110 
2112 }
2113 
2114 
2115 static string GetErrorMsg() {
2116  if (g_boot_error)
2117  return *g_boot_error;
2118  return "";
2119 }
2120 
2121 
2127 static void ShutdownMountpoint() {
2128  delete cvmfs::talk_mgr_;
2129  cvmfs::talk_mgr_ = NULL;
2130 
2133 
2134  // The remonter has a reference to the mount point and the inode generation
2135  delete cvmfs::fuse_remounter_;
2136  cvmfs::fuse_remounter_ = NULL;
2137 
2138  // The unpin listener requires the catalog, so this must be unregistered
2139  // before the catalog manager is removed
2140  if (cvmfs::unpin_listener_ != NULL) {
2142  cvmfs::unpin_listener_ = NULL;
2143  }
2144  if (cvmfs::watchdog_listener_ != NULL) {
2147  }
2148 
2150  delete cvmfs::mount_point_;
2152  cvmfs::mount_point_ = NULL;
2153 }
2154 
2155 
2156 static void Fini() {
2158 
2159  delete cvmfs::file_system_;
2160  delete cvmfs::options_mgr_;
2161  cvmfs::file_system_ = NULL;
2162  cvmfs::options_mgr_ = NULL;
2163 
2164  delete cvmfs::watchdog_;
2165  cvmfs::watchdog_ = NULL;
2166 
2167  delete g_boot_error;
2168  g_boot_error = NULL;
2170 }
2171 
2172 
2173 static int AltProcessFlavor(int argc, char **argv) {
2174  if (strcmp(argv[1], "__cachemgr__") == 0) {
2175  return PosixQuotaManager::MainCacheManager(argc, argv);
2176  }
2177  if (strcmp(argv[1], "__wpad__") == 0) {
2178  return download::MainResolveProxyDescription(argc, argv);
2179  }
2180  return 1;
2181 }
2182 
2183 
2184 static bool MaintenanceMode(const int fd_progress) {
2185  SendMsg2Socket(fd_progress, "Entering maintenance mode\n");
2186  string msg_progress = "Draining out kernel caches (";
2188  msg_progress += "up to ";
2189  msg_progress += StringifyInt(static_cast<int>(
2190  cvmfs::mount_point_->kcache_timeout_sec())) +
2191  "s)\n";
2192  SendMsg2Socket(fd_progress, msg_progress);
2194  return true;
2195 }
2196 
2197 
2198 static bool SaveState(const int fd_progress, loader::StateList *saved_states) {
2199  string msg_progress;
2200 
2201  unsigned num_open_dirs = cvmfs::directory_handles_->size();
2202  if (num_open_dirs != 0) {
2203 #ifdef DEBUGMSG
2204  for (cvmfs::DirectoryHandles::iterator i =
2205  cvmfs::directory_handles_->begin(),
2206  iEnd = cvmfs::directory_handles_->end(); i != iEnd; ++i)
2207  {
2208  LogCvmfs(kLogCvmfs, kLogDebug, "saving dirhandle %d", i->first);
2209  }
2210 #endif
2211 
2212  msg_progress = "Saving open directory handles (" +
2213  StringifyInt(num_open_dirs) + " handles)\n";
2214  SendMsg2Socket(fd_progress, msg_progress);
2215 
2216  // TODO(jblomer): should rather be saved just in a malloc'd memory block
2217  cvmfs::DirectoryHandles *saved_handles =
2219  loader::SavedState *save_open_dirs = new loader::SavedState();
2220  save_open_dirs->state_id = loader::kStateOpenDirs;
2221  save_open_dirs->state = saved_handles;
2222  saved_states->push_back(save_open_dirs);
2223  }
2224 
2225  if (!cvmfs::file_system_->IsNfsSource()) {
2226  msg_progress = "Saving inode tracker\n";
2227  SendMsg2Socket(fd_progress, msg_progress);
2228  glue::InodeTracker *saved_inode_tracker =
2229  new glue::InodeTracker(*cvmfs::mount_point_->inode_tracker());
2230  loader::SavedState *state_glue_buffer = new loader::SavedState();
2231  state_glue_buffer->state_id = loader::kStateGlueBufferV4;
2232  state_glue_buffer->state = saved_inode_tracker;
2233  saved_states->push_back(state_glue_buffer);
2234  }
2235 
2236  msg_progress = "Saving negative entry cache\n";
2237  SendMsg2Socket(fd_progress, msg_progress);
2238  glue::DentryTracker *saved_dentry_tracker =
2239  new glue::DentryTracker(*cvmfs::mount_point_->dentry_tracker());
2240  loader::SavedState *state_dentry_tracker = new loader::SavedState();
2241  state_dentry_tracker->state_id = loader::kStateDentryTracker;
2242  state_dentry_tracker->state = saved_dentry_tracker;
2243  saved_states->push_back(state_dentry_tracker);
2244 
2245  msg_progress = "Saving page cache entry tracker\n";
2246  SendMsg2Socket(fd_progress, msg_progress);
2247  glue::PageCacheTracker *saved_page_cache_tracker =
2248  new glue::PageCacheTracker(*cvmfs::mount_point_->page_cache_tracker());
2249  loader::SavedState *state_page_cache_tracker = new loader::SavedState();
2250  state_page_cache_tracker->state_id = loader::kStatePageCacheTracker;
2251  state_page_cache_tracker->state = saved_page_cache_tracker;
2252  saved_states->push_back(state_page_cache_tracker);
2253 
2254  msg_progress = "Saving chunk tables\n";
2255  SendMsg2Socket(fd_progress, msg_progress);
2256  ChunkTables *saved_chunk_tables = new ChunkTables(
2257  *cvmfs::mount_point_->chunk_tables());
2258  loader::SavedState *state_chunk_tables = new loader::SavedState();
2259  state_chunk_tables->state_id = loader::kStateOpenChunksV4;
2260  state_chunk_tables->state = saved_chunk_tables;
2261  saved_states->push_back(state_chunk_tables);
2262 
2263  msg_progress = "Saving inode generation\n";
2264  SendMsg2Socket(fd_progress, msg_progress);
2267  cvmfs::InodeGenerationInfo *saved_inode_generation =
2269  loader::SavedState *state_inode_generation = new loader::SavedState();
2270  state_inode_generation->state_id = loader::kStateInodeGeneration;
2271  state_inode_generation->state = saved_inode_generation;
2272  saved_states->push_back(state_inode_generation);
2273 
2274  // Close open file catalogs
2276 
2277  loader::SavedState *state_cache_mgr = new loader::SavedState();
2278  state_cache_mgr->state_id = loader::kStateOpenFiles;
2279  state_cache_mgr->state =
2280  cvmfs::file_system_->cache_mgr()->SaveState(fd_progress);
2281  saved_states->push_back(state_cache_mgr);
2282 
2283  msg_progress = "Saving open files counter\n";
2284  uint32_t *saved_num_fd =
2285  new uint32_t(cvmfs::file_system_->no_open_files()->Get());
2286  loader::SavedState *state_num_fd = new loader::SavedState();
2287  state_num_fd->state_id = loader::kStateOpenFilesCounter;
2288  state_num_fd->state = saved_num_fd;
2289  saved_states->push_back(state_num_fd);
2290 
2291  return true;
2292 }
2293 
2294 
2295 static bool RestoreState(const int fd_progress,
2296  const loader::StateList &saved_states)
2297 {
2298  // If we have no saved version of the page cache tracker, it is unsafe
2299  // to start using it. The page cache tracker has to run for the entire
2300  // lifetime of the mountpoint or not at all.
2302 
2303  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2304  if (saved_states[i]->state_id == loader::kStateOpenDirs) {
2305  SendMsg2Socket(fd_progress, "Restoring open directory handles... ");
2307  cvmfs::DirectoryHandles *saved_handles =
2308  (cvmfs::DirectoryHandles *)saved_states[i]->state;
2309  cvmfs::directory_handles_ = new cvmfs::DirectoryHandles(*saved_handles);
2312  cvmfs::DirectoryHandles::const_iterator i =
2313  cvmfs::directory_handles_->begin();
2314  for (; i != cvmfs::directory_handles_->end(); ++i) {
2315  if (i->first >= cvmfs::next_directory_handle_)
2316  cvmfs::next_directory_handle_ = i->first + 1;
2317  }
2318 
2319  SendMsg2Socket(fd_progress,
2320  StringifyInt(cvmfs::directory_handles_->size()) + " handles\n");
2321  }
2322 
2323  if (saved_states[i]->state_id == loader::kStateGlueBuffer) {
2324  SendMsg2Socket(fd_progress, "Migrating inode tracker (v1 to v4)... ");
2325  compat::inode_tracker::InodeTracker *saved_inode_tracker =
2326  (compat::inode_tracker::InodeTracker *)saved_states[i]->state;
2328  saved_inode_tracker, cvmfs::mount_point_->inode_tracker());
2329  SendMsg2Socket(fd_progress, " done\n");
2330  }
2331 
2332  if (saved_states[i]->state_id == loader::kStateGlueBufferV2) {
2333  SendMsg2Socket(fd_progress, "Migrating inode tracker (v2 to v4)... ");
2334  compat::inode_tracker_v2::InodeTracker *saved_inode_tracker =
2335  (compat::inode_tracker_v2::InodeTracker *)saved_states[i]->state;
2336  compat::inode_tracker_v2::Migrate(saved_inode_tracker,
2337  cvmfs::mount_point_->inode_tracker());
2338  SendMsg2Socket(fd_progress, " done\n");
2339  }
2340 
2341  if (saved_states[i]->state_id == loader::kStateGlueBufferV3) {
2342  SendMsg2Socket(fd_progress, "Migrating inode tracker (v3 to v4)... ");
2343  compat::inode_tracker_v3::InodeTracker *saved_inode_tracker =
2344  (compat::inode_tracker_v3::InodeTracker *)saved_states[i]->state;
2345  compat::inode_tracker_v3::Migrate(saved_inode_tracker,
2346  cvmfs::mount_point_->inode_tracker());
2347  SendMsg2Socket(fd_progress, " done\n");
2348  }
2349 
2350  if (saved_states[i]->state_id == loader::kStateGlueBufferV4) {
2351  SendMsg2Socket(fd_progress, "Restoring inode tracker... ");
2353  glue::InodeTracker *saved_inode_tracker =
2354  (glue::InodeTracker *)saved_states[i]->state;
2356  glue::InodeTracker(*saved_inode_tracker);
2357  SendMsg2Socket(fd_progress, " done\n");
2358  }
2359 
2360  if (saved_states[i]->state_id == loader::kStateDentryTracker) {
2361  SendMsg2Socket(fd_progress, "Restoring dentry tracker... ");
2363  glue::DentryTracker *saved_dentry_tracker =
2364  static_cast<glue::DentryTracker *>(saved_states[i]->state);
2366  glue::DentryTracker(*saved_dentry_tracker);
2367  SendMsg2Socket(fd_progress, " done\n");
2368  }
2369 
2370  if (saved_states[i]->state_id == loader::kStatePageCacheTracker) {
2371  SendMsg2Socket(fd_progress, "Restoring page cache entry tracker... ");
2373  glue::PageCacheTracker *saved_page_cache_tracker =
2374  (glue::PageCacheTracker *)saved_states[i]->state;
2376  glue::PageCacheTracker(*saved_page_cache_tracker);
2377  SendMsg2Socket(fd_progress, " done\n");
2378  }
2379 
2380  ChunkTables *chunk_tables = cvmfs::mount_point_->chunk_tables();
2381 
2382  if (saved_states[i]->state_id == loader::kStateOpenChunks) {
2383  SendMsg2Socket(fd_progress, "Migrating chunk tables (v1 to v4)... ");
2384  compat::chunk_tables::ChunkTables *saved_chunk_tables =
2385  (compat::chunk_tables::ChunkTables *)saved_states[i]->state;
2386  compat::chunk_tables::Migrate(saved_chunk_tables, chunk_tables);
2387  SendMsg2Socket(fd_progress,
2388  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2389  }
2390 
2391  if (saved_states[i]->state_id == loader::kStateOpenChunksV2) {
2392  SendMsg2Socket(fd_progress, "Migrating chunk tables (v2 to v4)... ");
2393  compat::chunk_tables_v2::ChunkTables *saved_chunk_tables =
2394  (compat::chunk_tables_v2::ChunkTables *)saved_states[i]->state;
2395  compat::chunk_tables_v2::Migrate(saved_chunk_tables, chunk_tables);
2396  SendMsg2Socket(fd_progress,
2397  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2398  }
2399 
2400  if (saved_states[i]->state_id == loader::kStateOpenChunksV3) {
2401  SendMsg2Socket(fd_progress, "Migrating chunk tables (v3 to v4)... ");
2402  compat::chunk_tables_v3::ChunkTables *saved_chunk_tables =
2403  (compat::chunk_tables_v3::ChunkTables *)saved_states[i]->state;
2404  compat::chunk_tables_v3::Migrate(saved_chunk_tables, chunk_tables);
2405  SendMsg2Socket(fd_progress,
2406  StringifyInt(chunk_tables->handle2fd.size()) + " handles\n");
2407  }
2408 
2409  if (saved_states[i]->state_id == loader::kStateOpenChunksV4) {
2410  SendMsg2Socket(fd_progress, "Restoring chunk tables... ");
2411  chunk_tables->~ChunkTables();
2412  ChunkTables *saved_chunk_tables = reinterpret_cast<ChunkTables *>(
2413  saved_states[i]->state);
2414  new (chunk_tables) ChunkTables(*saved_chunk_tables);
2415  SendMsg2Socket(fd_progress, " done\n");
2416  }
2417 
2418  if (saved_states[i]->state_id == loader::kStateInodeGeneration) {
2419  SendMsg2Socket(fd_progress, "Restoring inode generation... ");
2420  cvmfs::InodeGenerationInfo *old_info =
2421  (cvmfs::InodeGenerationInfo *)saved_states[i]->state;
2422  if (old_info->version == 1) {
2423  // Migration
2425  old_info->initial_revision;
2427  // Note: in the rare case of inode generation being 0 before, inode
2428  // can clash after reload before remount
2429  } else {
2430  cvmfs::inode_generation_info_ = *old_info;
2431  }
2433  SendMsg2Socket(fd_progress, " done\n");
2434  }
2435 
2436  if (saved_states[i]->state_id == loader::kStateOpenFilesCounter) {
2437  SendMsg2Socket(fd_progress, "Restoring open files counter... ");
2438  cvmfs::file_system_->no_open_files()->Set(*(reinterpret_cast<uint32_t *>(
2439  saved_states[i]->state)));
2440  SendMsg2Socket(fd_progress, " done\n");
2441  }
2442 
2443  if (saved_states[i]->state_id == loader::kStateOpenFiles) {
2444  int new_root_fd = cvmfs::file_system_->cache_mgr()->RestoreState(
2445  fd_progress, saved_states[i]->state);
2446  LogCvmfs(kLogCvmfs, kLogDebug, "new root file catalog descriptor @%d",
2447  new_root_fd);
2448  if (new_root_fd >= 0) {
2449  cvmfs::file_system_->RemapCatalogFd(0, new_root_fd);
2450  }
2451  }
2452  }
2453  if (cvmfs::mount_point_->inode_annotation()) {
2454  uint64_t saved_generation = cvmfs::inode_generation_info_.inode_generation;
2455  cvmfs::mount_point_->inode_annotation()->IncGeneration(saved_generation);
2456  }
2457 
2458  return true;
2459 }
2460 
2461 
2462 static void FreeSavedState(const int fd_progress,
2463  const loader::StateList &saved_states)
2464 {
2465  for (unsigned i = 0, l = saved_states.size(); i < l; ++i) {
2466  switch (saved_states[i]->state_id) {
2468  SendMsg2Socket(fd_progress, "Releasing saved open directory handles\n");
2469  delete static_cast<cvmfs::DirectoryHandles *>(saved_states[i]->state);
2470  break;
2473  fd_progress, "Releasing saved glue buffer (version 1)\n");
2474  delete static_cast<compat::inode_tracker::InodeTracker *>(
2475  saved_states[i]->state);
2476  break;
2479  fd_progress, "Releasing saved glue buffer (version 2)\n");
2480  delete static_cast<compat::inode_tracker_v2::InodeTracker *>(
2481  saved_states[i]->state);
2482  break;
2485  fd_progress, "Releasing saved glue buffer (version 3)\n");
2486  delete static_cast<compat::inode_tracker_v3::InodeTracker *>(
2487  saved_states[i]->state);
2488  break;
2490  SendMsg2Socket(fd_progress, "Releasing saved glue buffer\n");
2491  delete static_cast<glue::InodeTracker *>(saved_states[i]->state);
2492  break;
2494  SendMsg2Socket(fd_progress, "Releasing saved dentry tracker\n");
2495  delete static_cast<glue::DentryTracker *>(saved_states[i]->state);
2496  break;
2498  SendMsg2Socket(fd_progress, "Releasing saved page cache entry cache\n");
2499  delete static_cast<glue::PageCacheTracker *>(saved_states[i]->state);
2500  break;
2502  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 1)\n");
2503  delete static_cast<compat::chunk_tables::ChunkTables *>(
2504  saved_states[i]->state);
2505  break;
2507  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 2)\n");
2508  delete static_cast<compat::chunk_tables_v2::ChunkTables *>(
2509  saved_states[i]->state);
2510  break;
2512  SendMsg2Socket(fd_progress, "Releasing chunk tables (version 3)\n");
2513  delete static_cast<compat::chunk_tables_v3::ChunkTables *>(
2514  saved_states[i]->state);
2515  break;
2517  SendMsg2Socket(fd_progress, "Releasing chunk tables\n");
2518  delete static_cast<ChunkTables *>(saved_states[i]->state);
2519  break;
2521  SendMsg2Socket(fd_progress, "Releasing saved inode generation info\n");
2522  delete static_cast<cvmfs::InodeGenerationInfo *>(
2523  saved_states[i]->state);
2524  break;
2527  fd_progress, saved_states[i]->state);
2528  break;
2530  SendMsg2Socket(fd_progress, "Releasing open files counter\n");
2531  delete static_cast<uint32_t *>(saved_states[i]->state);
2532  break;
2533  default:
2534  break;
2535  }
2536  }
2537 }
2538 
2539 
2540 static void __attribute__((constructor)) LibraryMain() {
2541  g_cvmfs_exports = new loader::CvmfsExports();
2542  g_cvmfs_exports->so_version = PACKAGE_VERSION;
2543  g_cvmfs_exports->fnAltProcessFlavor = AltProcessFlavor;
2544  g_cvmfs_exports->fnInit = Init;
2545  g_cvmfs_exports->fnSpawn = Spawn;
2546  g_cvmfs_exports->fnFini = Fini;
2547  g_cvmfs_exports->fnGetErrorMsg = GetErrorMsg;
2548  g_cvmfs_exports->fnMaintenanceMode = MaintenanceMode;
2549  g_cvmfs_exports->fnSaveState = SaveState;
2550  g_cvmfs_exports->fnRestoreState = RestoreState;
2551  g_cvmfs_exports->fnFreeSavedState = FreeSavedState;
2552  cvmfs::SetCvmfsOperations(&g_cvmfs_exports->cvmfs_operations);
2553 }
2554 
2555 
2556 static void __attribute__((destructor)) LibraryExit() {
2557  delete g_cvmfs_exports;
2558  g_cvmfs_exports = NULL;
2559 }
bool GetInfoIfOpen(uint64_t inode, shash::Any *hash, struct stat *info)
Definition: glue_buffer.h:1025
VfsPutRaii GetVfsPutRaii()
Definition: glue_buffer.h:688
std::string repository_name
Definition: loader.h:177
OptionsManager * options_mgr()
Definition: mountpoint.h:226
#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:223
bool IsExternalFile() const
std::string mount_point
Definition: loader.h:178
bool IsInDrainoutMode()
Definition: fuse_remount.h:64
void UnregisterQuotaListener()
Definition: cvmfs.cc:1790
static const time_t kIndefiniteDeadline
Definition: mountpoint.h:420
perf::Counter * n_fs_inode_replace()
Definition: mountpoint.h:213
virtual std::string GetValue()
Definition: cvmfs.cc:1842
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:678
std::string ListKeysPosix(const std::string &merge_with) const
Definition: xattr.cc:137
FileSystem * file_system()
Definition: mountpoint.h:447
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:204
time_t catalogs_valid_until_
Definition: cvmfs.cc:1835
static bool HasDifferentContent(const catalog::DirectoryEntry &dirent, const shash::Any &hash, const struct stat &info)
Definition: cvmfs.cc:228
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:1248
time_t mtime() const
static double GetKcacheTimeout()
Definition: cvmfs.cc:176
void EnterMaintenanceMode()
void set_inode(const inode_t inode)
InodeGenerationInfo inode_generation_info_
Definition: cvmfs.cc:127
loader::Failures boot_status()
Definition: mountpoint.h:75
static void FreeSavedState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2462
bool IsDirectory() const
static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino)
Definition: cvmfs.cc:1464
void set_boot_status(loader::Failures code)
Definition: mountpoint.h:82
static void DoTraceInode(const int event, fuse_ino_t ino, const std::string &msg)
Definition: cvmfs.cc:437
cvmfs::Fetcher * fetcher()
Definition: mountpoint.h:443
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:937
bool IsChunkedFile() const
FuseRemounter * fuse_remounter_
Definition: cvmfs.cc:126
static Watchdog * Create(const std::string &crash_dump_path)
Definition: monitor.cc:60
uid_t talk_socket_uid()
Definition: mountpoint.h:466
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:124
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:154
#define PANIC(...)
Definition: exception.h:27
void Spawn()
Definition: tracer.cc:212
SpecialDirents GetSpecial() const
virtual void Spawn()=0
static int AltProcessFlavor(int argc, char **argv)
Definition: cvmfs.cc:2173
uint64_t size() const
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:249
double kcache_timeout_sec()
Definition: mountpoint.h:456
void Assign(const char *chars, const unsigned length)
Definition: shortstring.h:53
static void cvmfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
Definition: cvmfs.cc:577
std::string PrintInodeGeneration()
Definition: cvmfs.cc:199
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:218
std::string fqrn() const
Definition: mountpoint.h:445
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:1396
BaseMagicXattr * GetLocked(const std::string &name, PathString path, catalog::DirectoryEntry *d)
Definition: magic_xattr.cc:108
Watchdog * watchdog_
Definition: cvmfs.cc:125
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:455
NotificationClient * notification_client_
Definition: cvmfs.cc:124
void * SaveState(const int fd_progress)
Definition: cache.cc:221
DirectoryHandles * directory_handles_
Definition: cvmfs.cc:155
StateId state_id
Definition: loader.h:122
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:2198
void Spawn()
Definition: monitor.cc:387
inode_t inode() const
void SendMsg2Socket(const int fd, const std::string &msg)
Definition: posix.cc:654
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:122
MagicXattrManager * magic_xattr_mgr()
Definition: mountpoint.h:448
std::string program_name
Definition: loader.h:180
Log2Histogram * hist_fs_read()
Definition: mountpoint.h:208
bool IsDirectIo() const
bool SendFd2Socket(int socket_fd, int passing_fd)
Definition: posix.cc:663
bool LookupPath(const PathString &path, const LookupOptions options, DirectoryEntry *entry)
static bool RestoreState(const int fd_progress, const loader::StateList &saved_states)
Definition: cvmfs.cc:2295
virtual uint64_t GetSize()=0
perf::Counter * n_fs_read()
Definition: mountpoint.h:217
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:457
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:449
virtual bool PrepareValueFenced()
Definition: cvmfs.cc:1837
virtual std::string GetValue()
Definition: cvmfs.cc:1853
unsigned int mode() const
struct cvmcache_object_info __attribute__
Definition: atomic.h:24
void RemapCatalogFd(int from, int to)
Definition: mountpoint.cc:1075
bool Pin(const string &path)
Definition: cvmfs.cc:1659
static void cvmfs_destroy(void *unused __attribute__((unused)))
Definition: cvmfs.cc:1756
static TalkManager * Create(const std::string &socket_path, MountPoint *mount_point, FuseRemounter *remounter)
Definition: talk.cc:78
unsigned max_open_files_
Definition: cvmfs.cc:159
static bool HasFuseNotifyInval()
Definition: fuse_evict.cc:53
perf::Counter * n_fs_open()
Definition: mountpoint.h:216
static void cvmfs_init(void *userdata, struct fuse_conn_info *conn)
Definition: cvmfs.cc:1731
TalkManager * talk_mgr_
Definition: cvmfs.cc:123
virtual uint64_t GetCapacity()=0
std::string membership_req()
Definition: mountpoint.h:458
void Throttle()
Definition: backoff.cc:50
std::string GetListString(catalog::DirectoryEntry *dirent)
Definition: magic_xattr.cc:69
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:157
void Unlock()
Definition: file_chunk.h:106
static void InitOptionsMgr(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:1930
Fence * fence()
Definition: fuse_remount.h:67
pid_t pid_
Definition: cvmfs.cc:147
PathString path
Definition: file_chunk.h:70
static bool GetDirentForInode(const fuse_ino_t ino, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:275
glue::PageCacheTracker * page_cache_tracker()
Definition: mountpoint.h:460
bool IsLink() const
void SetBit(unsigned int bit, T *field)
Definition: algorithm.h:30
BigVector< FileChunk > FileChunkList
Definition: file_chunk.h:51
virtual void Spawn()=0
static bool FixupOpenInode(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:252
bool HasXattrs() const
virtual std::string GetValue()
Definition: cvmfs.cc:1871
static void TraceInode(const int event, fuse_ino_t ino, const std::string &msg)
Definition: cvmfs.cc:452
bool Get(const std::string &key, std::string *value) const
Definition: xattr.cc:108
catalog::ClientCatalogManager * catalog_mgr()
Definition: mountpoint.h:434
void Set(const int64_t val)
Definition: statistics.h:33
Log2Histogram * hist_fs_forget_multi()
Definition: mountpoint.h:201
static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
Definition: cvmfs.cc:1598
bool IsRegular() const
vector< string > SplitString(const string &str, char delim)
Definition: string.cc:288
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:223
Log2Histogram * hist_fs_release()
Definition: mountpoint.h:209
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:145
EVisibility visibility()
Definition: magic_xattr.h:154
virtual inode_t GetGeneration()=0
std::string config_files
Definition: loader.h:179
perf::Counter * no_open_dirs()
Definition: mountpoint.h:224
catalog::InodeAnnotation * inode_annotation()
Definition: mountpoint.h:451
Log2Histogram * hist_fs_releasedir()
Definition: mountpoint.h:205
quota::ListenerHandle * watchdog_listener_
Definition: cvmfs.cc:148
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:170
void SetSize(const size_t new_size)
Definition: bigvector.h:116
std::string boot_error()
Definition: mountpoint.h:76
off_t offset() const
Definition: file_chunk.h:42
lru::PathCache * path_cache()
Definition: mountpoint.h:461
virtual std::string GetValue()
Definition: cvmfs.cc:1861
static void cvmfs_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:1027
zlib::Algorithms compression_algorithm() const
unsigned GetMaxOpenFiles()
Definition: monitor.cc:646
bool IsNfsSource()
Definition: mountpoint.h:189
CacheManager * cache_mgr()
Definition: mountpoint.h:195
const signature::SignatureManager * signature_mgr() const
Definition: repository.h:121
file_watcher::FileWatcher * resolv_conf_watcher()
Definition: mountpoint.h:440
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:1867
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:2127
unsigned FindChunkIdx(const uint64_t offset)
Definition: file_chunk.cc:23
bool SendFuseFd(const std::string &socket_path)
Definition: cvmfs.cc:1801
perf::Counter * no_open_files()
Definition: mountpoint.h:225
AuthzSessionManager * authz_session_mgr()
Definition: mountpoint.h:432
LinkString symlink() const
void Insert(const Key &key, const Value &value)
Definition: smallhash.h:109
IoErrorInfo * io_error_info()
Definition: mountpoint.h:221
download::DownloadManager * download_mgr()
Definition: mountpoint.h:436
perf::Counter * n_fs_stat_stale()
Definition: mountpoint.h:220
static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations)
Definition: cvmfs.cc:1764
bool simple_options_parsing
Definition: loader.h:188
perf::Counter * n_fs_forget()
Definition: mountpoint.h:212
Log2Histogram * hist_fs_readlink()
Definition: mountpoint.h:203
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:1950
void Append(const char *chars, const unsigned length)
Definition: shortstring.h:70
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
static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino)
Definition: cvmfs.cc:747
size_t capacity() const
Definition: bigvector.h:122
static void ReplyNegative(const catalog::DirectoryEntry &dirent, fuse_req_t req)
Definition: cvmfs.cc:665
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:986
ChunkTables * chunk_tables()
Definition: mountpoint.h:435
pthread_mutex_t lock_directory_handles_
Definition: cvmfs.cc:156
static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
Definition: cvmfs.cc:806
OptionsManager * options_mgr_
Definition: cvmfs.cc:146
virtual bool GetPath(const uint64_t inode, PathString *path)=0
shash::Algorithms hash_algorithm() const
glue::DentryTracker * dentry_tracker()
Definition: mountpoint.h:459
static int MainCacheManager(int argc, char **argv)
Definition: quota_posix.cc:952
perf::Counter * n_fs_dir_open()
Definition: mountpoint.h:211
Log2Histogram * hist_fs_readdir()
Definition: mountpoint.h:206
cvmfs::Fetcher * external_fetcher()
Definition: mountpoint.h:446
bool enforce_acls()
Definition: mountpoint.h:450
bool IsInMaintenanceMode()
Definition: fuse_remount.h:65
static bool CheckVoms(const fuse_ctx &fctx)
Definition: cvmfs.cc:210
static void FillOpenFlags(const glue::PageCacheTracker::OpenDirectives od, struct fuse_file_info *fi)
Definition: cvmfs.cc:1010
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:207
OpenDirectives Open(uint64_t inode, const shash::Any &hash, const struct stat &info)
Definition: glue_buffer.cc:316
std::string ToString() const
Definition: shortstring.h:114
QuotaManager * quota_mgr()
Definition: cache.h:198
static string GetErrorMsg()
Definition: cvmfs.cc:2115
static uint64_t GetDirentForPath(const PathString &path, catalog::DirectoryEntry *dirent)
Definition: cvmfs.cc:360
int ConnectSocket(const std::string &path)
Definition: posix.cc:422
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:199
bool IsCompatibleFileType(unsigned mode) const
Definition: glue_buffer.h:85
bool TestBit(unsigned int bit, const T field)
Definition: algorithm.h:40
void Close(uint64_t inode)
Definition: glue_buffer.cc:398
string * g_boot_error
Definition: cvmfs.cc:1825
bool ListFileChunks(const PathString &path, const shash::Algorithms interpret_hashes_as, FileChunkList *chunks)
static void Fini()
Definition: cvmfs.cc:2156
perf::Counter * n_fs_lookup()
Definition: mountpoint.h:214
static void Spawn()
Definition: cvmfs.cc:2071
static bool UseWatchdog()
Definition: cvmfs.cc:189
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:190
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:168
void UnregisterListener(ListenerHandle *handle)
Definition: mutex.h:42
bool Evict(const string &path)
Definition: cvmfs.cc:1646
std::vector< SavedState * > StateList
Definition: loader.h:125
PathString GetParentPath(const PathString &path)
Definition: shortstring.cc:15
FileSystem * file_system_
Definition: cvmfs.cc:121
EvictRaii GetEvictRaii()
Definition: glue_buffer.h:1040
virtual bool PrepareValueFenced()
Definition: magic_xattr.h:52
void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode)
Definition: cvmfs.cc:183
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:215
virtual uint64_t GetInode(const PathString &path)=0
Log2Histogram * hist_fs_forget()
Definition: mountpoint.h:200
const int kNumReservedFd
Definition: cvmfs.cc:163
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:970
unsigned GetLength() const
Definition: shortstring.h:104
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:433
Log2Histogram * hist_fs_getattr()
Definition: mountpoint.h:202
gid_t talk_socket_gid()
Definition: mountpoint.h:467
const char * c_str() const
Definition: shortstring.h:118
static bool GetPathForInode(const fuse_ino_t ino, PathString *path)
Definition: cvmfs.cc:411
static const unsigned int kBitDirectIo
Definition: glue_buffer.h:950
const char * GetChars() const
Definition: shortstring.h:96
static void RegisterMagicXattrs()
Definition: cvmfs.cc:1882
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:454
bool Insert(const shash::Md5 &hash, const catalog::DirectoryEntry &dirent)
Definition: lru_md.h:121
perf::Counter * n_fs_stat()
Definition: mountpoint.h:219
static void size_t size
Definition: smalloc.h:47
virtual ~FuseInterruptCue()
Definition: cvmfs.cc:169
static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
Definition: cvmfs.cc:464
#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:2184
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:1895
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:1515
Tracer * tracer()
Definition: mountpoint.h:469
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:779
size_t size() const
Definition: bigvector.h:121
download::DownloadManager * external_download_mgr()
Definition: mountpoint.h:437
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
quota::ListenerHandle * unpin_listener_
Definition: cvmfs.cc:149
static MountPoint * Create(const std::string &fqrn, FileSystem *file_system, OptionsManager *options_mgr=NULL)
Definition: mountpoint.cc:1161
void Spawn()
Definition: talk.cc:843
fuse_req_t * req_ptr_
Definition: cvmfs.cc:172
OptionsManager * options_mgr
Definition: mountpoint.h:137