summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Rosdahl <joel@rosdahl.net>2023-01-23 22:14:54 +0100
committerJoel Rosdahl <joel@rosdahl.net>2023-01-29 21:19:59 +0100
commitde63795267aba8ba91478cc761d12aa36d530619 (patch)
tree66686b251e4fa59f7ebedbf2335c33980647414e
parent7fce6fa9c184c758937413f97c12e5c13794d667 (diff)
downloadccache-de63795267aba8ba91478cc761d12aa36d530619.tar.gz
fix: Disable inode cache if filesystem risks getting full soon
Some filesystems, for instance btrfs with compression enabled, apparently make a posix_fallocate call succeed without actually allocating the requested space for the file. This means that if the file is mapped into memory, like done by the inode cache, the process can crash when accessing the memory if the filesystem is full. This commit implements a workaround: the inode cache is disabled if the filesystem reports that it has less than 100 MiB free space. The free space check is valid for one second before it is done again. This should hopefully make crashes very rare in practice. Closes #1236.
-rw-r--r--src/InodeCache.cpp45
-rw-r--r--src/InodeCache.hpp6
2 files changed, 41 insertions, 10 deletions
diff --git a/src/InodeCache.cpp b/src/InodeCache.cpp
index e6245fa6..17e92417 100644
--- a/src/InodeCache.cpp
+++ b/src/InodeCache.cpp
@@ -20,7 +20,6 @@
#include "Config.hpp"
#include "Digest.hpp"
-#include "Fd.hpp"
#include "Finalizer.hpp"
#include "Hash.hpp"
#include "Logging.hpp"
@@ -29,8 +28,6 @@
#include "Util.hpp"
#include "fmtmacros.hpp"
-#include <util/TimePoint.hpp>
-
#include <fcntl.h>
#include <libgen.h>
#include <sched.h>
@@ -78,6 +75,14 @@ const uint32_t k_num_entries = 4;
// Maximum time the spin lock loop will try before giving up.
const auto k_max_lock_duration = util::Duration(5);
+// The memory-mapped file may reside on a filesystem with compression. Memory
+// accesses to the file risk crashing if such a filesystem gets full, so stop
+// using the inode cache well before this happens.
+const uint64_t k_min_fs_mib_left = 100; // 100 MiB
+
+// How long a filesystem space check is valid before we make a new one.
+const util::Duration k_fs_space_check_valid_duration(1);
+
static_assert(Digest::size() == 20,
"Increment version number if size of digest is changed.");
static_assert(std::is_trivially_copyable<Digest>::value,
@@ -229,17 +234,21 @@ InodeCache::mmap_file(const std::string& inode_cache_file)
munmap(m_sr, sizeof(SharedRegion));
m_sr = nullptr;
}
- Fd fd(open(inode_cache_file.c_str(), O_RDWR));
- if (!fd) {
+ m_fd = Fd(open(inode_cache_file.c_str(), O_RDWR));
+ if (!m_fd) {
LOG("Failed to open inode cache {}: {}", inode_cache_file, strerror(errno));
return false;
}
- if (!fd_is_on_known_to_work_file_system(*fd)) {
+ if (!fd_is_on_known_to_work_file_system(*m_fd)) {
return false;
}
- SharedRegion* sr = reinterpret_cast<SharedRegion*>(mmap(
- nullptr, sizeof(SharedRegion), PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0));
- fd.close();
+ SharedRegion* sr =
+ reinterpret_cast<SharedRegion*>(mmap(nullptr,
+ sizeof(SharedRegion),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ *m_fd,
+ 0));
if (sr == MMAP_FAILED) {
LOG("Failed to mmap {}: {}", inode_cache_file, strerror(errno));
return false;
@@ -387,6 +396,24 @@ InodeCache::initialize()
return false;
}
+ if (m_fd) {
+ auto now = util::TimePoint::now();
+ if (now > m_last_fs_space_check + k_fs_space_check_valid_duration) {
+ m_last_fs_space_check = now;
+
+ struct statfs buf;
+ if (fstatfs(*m_fd, &buf) != 0) {
+ LOG("fstatfs failed: {}", strerror(errno));
+ return false;
+ }
+ if (buf.f_bavail * 512 < k_min_fs_mib_left * 1024 * 1024) {
+ LOG("Filesystem has less than {} MiB free space, not using inode cache",
+ k_min_fs_mib_left);
+ return false;
+ }
+ }
+ }
+
if (m_sr) {
return true;
}
diff --git a/src/InodeCache.hpp b/src/InodeCache.hpp
index c4e8c4ea..5819ed07 100644
--- a/src/InodeCache.hpp
+++ b/src/InodeCache.hpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2020-2022 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2023 Joel Rosdahl and other contributors
//
// See doc/AUTHORS.adoc for a complete list of contributors.
//
@@ -18,7 +18,9 @@
#pragma once
+#include <Fd.hpp>
#include <util/Duration.hpp>
+#include <util/TimePoint.hpp>
#include <cstdint>
#include <functional>
@@ -130,7 +132,9 @@ private:
const Config& m_config;
util::Duration m_min_age;
+ Fd m_fd;
struct SharedRegion* m_sr = nullptr;
bool m_failed = false;
const pid_t m_self_pid;
+ util::TimePoint m_last_fs_space_check;
};