diff options
author | Joel Rosdahl <joel@rosdahl.net> | 2023-01-23 22:14:54 +0100 |
---|---|---|
committer | Joel Rosdahl <joel@rosdahl.net> | 2023-01-29 21:19:59 +0100 |
commit | de63795267aba8ba91478cc761d12aa36d530619 (patch) | |
tree | 66686b251e4fa59f7ebedbf2335c33980647414e | |
parent | 7fce6fa9c184c758937413f97c12e5c13794d667 (diff) | |
download | ccache-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.cpp | 45 | ||||
-rw-r--r-- | src/InodeCache.hpp | 6 |
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; }; |