Stop trying to mmap the history file on remote fs

When the history file is on a remote filesystem, memory mapping is suspicious.
Never mmap in this case.
This commit is contained in:
ridiculousfish 2018-07-15 11:50:02 -07:00
parent 679437d6a9
commit 4d1eeef3db
2 changed files with 57 additions and 7 deletions

View File

@ -241,6 +241,38 @@ class history_file_contents_t {
history_file_contents_t(history_file_contents_t &&) = delete;
void operator=(history_file_contents_t &&) = delete;
// Check if we should mmap the fd.
// Don't try mmap() on non-local filesystems.
static bool should_mmap(int fd) {
if (history_t::never_mmap) return false;
// mmap only if we are known not-remote (return is 0).
int ret = fd_check_is_remote(fd);
return ret == 0;
}
// Read up to len bytes from fd into address, zeroing the rest.
// Return true on success, false on failure.
static bool read_from_fd(int fd, void *address, size_t len) {
size_t remaining = len;
char *ptr = static_cast<char *>(address);
while (remaining > 0) {
ssize_t amt = read(fd, ptr, remaining);
if (amt < 0) {
if (errno != EINTR) {
return false;
}
} else if (amt == 0) {
break;
} else {
remaining -= amt;
ptr += amt;
}
}
bzero(ptr, remaining);
return true;
}
public:
// Access the address at a given offset.
const char *address_at(size_t offset) const {
@ -270,9 +302,21 @@ class history_file_contents_t {
if (len <= 0 || len >= SIZE_MAX) return nullptr;
if (lseek(fd, 0, SEEK_SET) != 0) return nullptr;
// Map the file.
void *mmap_start = mmap(0, size_t(len), PROT_READ, MAP_PRIVATE, fd, 0);
if (mmap_start == MAP_FAILED) return nullptr;
// Read the file, possibly ussing mmap.
void *mmap_start = nullptr;
if (should_mmap(fd)) {
// We feel confident to map the file directly. Note this is still risky: if another
// process truncates the file we risk SIGBUS.
mmap_start = mmap(0, size_t(len), PROT_READ, MAP_PRIVATE, fd, 0);
if (mmap_start == MAP_FAILED) return nullptr;
} else {
// We don't want to map the file. mmap some private memory and then read into it. We use
// mmap instead of malloc so that the destructor can always munmap().
mmap_start =
mmap(0, size_t(len), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (mmap_start == MAP_FAILED) return nullptr;
if (!read_from_fd(fd, mmap_start, len)) return nullptr;
}
// Check the file type.
auto mtype = infer_file_type(mmap_start, len);
@ -798,6 +842,9 @@ history_t::history_t(wcstring pname)
history_t::~history_t() = default;
bool history_t::chaos_mode = false;
bool history_t::never_mmap = false;
void history_t::add(const history_item_t &item, bool pending) {
scoped_lock locker(lock);

View File

@ -169,10 +169,6 @@ class history_t {
// List of old items, as offsets into out mmap data.
std::deque<size_t> old_item_offsets;
// Whether we're in maximum chaos mode, useful for testing.
// This causes things like locks to fail.
bool chaos_mode{false};
// Figure out the offsets of our file contents.
void populate_from_file_contents();
@ -208,6 +204,13 @@ class history_t {
explicit history_t(wcstring name);
~history_t();
// Whether we're in maximum chaos mode, useful for testing.
// This causes things like locks to fail.
static bool chaos_mode;
// Whether to force the read path instead of mmap. This is useful for testing.
static bool never_mmap;
// Returns history with the given name, creating it if necessary.
static history_t &history_with_name(const wcstring &name);