Replace boost::algorithm::all_of_equal() to std::ranges::all_of() In order to reduce the header dependency to boost ranges library, let's use the utility from the standard library when appropriate. Signed-off-by: Kefu Chai <kefu.chai@scylladb.com> Closes scylladb/scylladb#22730
131 lines
5.5 KiB
C++
131 lines
5.5 KiB
C++
/*
|
|
* Copyright (C) 2017-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#include "integrity_checked_file_impl.hh"
|
|
#include <seastar/core/do_with.hh>
|
|
#include <seastar/core/format.hh>
|
|
#include "bytes.hh"
|
|
|
|
namespace sstables {
|
|
|
|
integrity_checked_file_impl::integrity_checked_file_impl(sstring fname, file f)
|
|
: file_impl(*get_file_impl(f)), _fname(std::move(fname)), _file(f) {
|
|
}
|
|
|
|
static bytes data_sample(const int8_t* buf, size_t buf_len, size_t sample_off, size_t sample_len) {
|
|
auto begin = buf + sample_off;
|
|
auto len = std::min(sample_len, buf_len - sample_off);
|
|
|
|
return bytes(begin, len);
|
|
}
|
|
|
|
static sstring report_zeroed_4k_aligned_blocks(const temporary_buffer<int8_t>& buf) {
|
|
sstring report;
|
|
auto nr_blocks = buf.size() / 4096UL;
|
|
for (auto i = 0UL; i < nr_blocks; i++) {
|
|
auto off = i * 4096UL;
|
|
auto len = std::min(buf.size() - off, 4096UL);
|
|
|
|
auto begin = buf.get() + off;
|
|
auto end = begin + len;
|
|
if (std::ranges::all_of(begin, end, [](auto byte) { return byte == 0; })) {
|
|
report += format("{:d}, ", off);
|
|
}
|
|
}
|
|
return report;
|
|
}
|
|
|
|
future<size_t>
|
|
integrity_checked_file_impl::write_dma(uint64_t pos, const void* buffer, size_t len, io_intent* intent) {
|
|
auto wbuf = temporary_buffer<int8_t>(static_cast<const int8_t*>(buffer), len);
|
|
|
|
auto ret = report_zeroed_4k_aligned_blocks(wbuf);
|
|
if (!ret.empty()) {
|
|
sstlog.warn("integrity check warning for {}, stage: before write started, write: {} bytes to offset {}, " \
|
|
"reason: 4k block(s) zeroed, follow their offsets: {}", _fname, len, pos, ret);
|
|
}
|
|
|
|
return get_file_impl(_file)->write_dma(pos, buffer, len, intent)
|
|
.then([this, pos, wbuf = std::move(wbuf), buffer = static_cast<const int8_t*>(buffer), len, intent] (size_t ret) mutable {
|
|
if (ret != len) {
|
|
sstlog.error("integrity check failed for {}, stage: after write finished, write: {} bytes to offset {}, " \
|
|
"reason: only {} bytes were written.", _fname, len, pos, ret);
|
|
}
|
|
|
|
auto wbuf_end = wbuf.get() + wbuf.size();
|
|
auto r = std::mismatch(wbuf.get(), wbuf_end, buffer, buffer + len);
|
|
if (r.first != wbuf_end) {
|
|
auto mismatch_off = r.first - wbuf.get();
|
|
|
|
sstlog.error("integrity check failed for {}, stage: after write verification, write: {} bytes to offset {}, " \
|
|
"reason: buffer was modified during write call, mismatch at byte {}:\n" \
|
|
" unmodified sample:\t{}\n" \
|
|
" modified sample: \t{}",
|
|
_fname, len, pos, mismatch_off,
|
|
data_sample(wbuf.get(), wbuf.size(), mismatch_off, 16), data_sample(buffer, len, mismatch_off, 16));
|
|
|
|
return make_ready_future<size_t>(ret);
|
|
}
|
|
|
|
return _file.dma_read_exactly<int8_t>(pos, len, intent).then([this, pos, wbuf = std::move(wbuf), len, ret] (auto rbuf) mutable {
|
|
if (rbuf.size() != len) {
|
|
sstlog.error("integrity check failed for {}, stage: read after write finished, write: {} bytes to offset {}, " \
|
|
"reason: only able to read {} bytes for further verification", _fname, len, pos, rbuf.size());
|
|
}
|
|
|
|
auto rbuf_end = rbuf.get() + rbuf.size();
|
|
auto r = std::mismatch(rbuf.get(), rbuf_end, wbuf.get(), wbuf.get() + wbuf.size());
|
|
if (r.first != rbuf_end) {
|
|
auto mismatch_off = r.first - rbuf.get();
|
|
|
|
sstlog.error("integrity check failed for {}, stage: read after write verification, write: {} bytes to offset {}, " \
|
|
"reason: data read from underlying storage isn't the same as written, mismatch at byte {}:\n" \
|
|
" data written sample:\t{}\n" \
|
|
" data read sample: \t{}",
|
|
_fname, len, pos, mismatch_off,
|
|
data_sample(wbuf.get(), wbuf.size(), mismatch_off, 16), data_sample(rbuf.get(), rbuf.size(), mismatch_off, 16));
|
|
}
|
|
return make_ready_future<size_t>(ret);
|
|
});
|
|
});
|
|
}
|
|
|
|
future<size_t>
|
|
integrity_checked_file_impl::write_dma(uint64_t pos, std::vector<iovec> iov, io_intent* intent) {
|
|
// TODO: check integrity before and after file_impl::write_dma() like write_dma() above.
|
|
return get_file_impl(_file)->write_dma(pos, iov, intent);
|
|
}
|
|
|
|
inline file make_integrity_checked_file(std::string_view name, file f) {
|
|
return file(::make_shared<integrity_checked_file_impl>(sstring(name), f));
|
|
}
|
|
|
|
inline open_flags adjust_flags_for_integrity_checked_file(open_flags flags) {
|
|
if (static_cast<unsigned int>(flags) & O_WRONLY) {
|
|
flags = open_flags((static_cast<unsigned int>(flags) & ~O_WRONLY) | O_RDWR);
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
future<file>
|
|
open_integrity_checked_file_dma(std::string_view name, open_flags flags, file_open_options options) noexcept {
|
|
static_assert(std::is_nothrow_move_constructible_v<file_open_options>);
|
|
return with_file_close_on_failure(open_file_dma(name, adjust_flags_for_integrity_checked_file(flags), std::move(options)), [name] (file f) {
|
|
return make_ready_future<file>(make_integrity_checked_file(name, f));
|
|
});
|
|
}
|
|
|
|
future<file>
|
|
open_integrity_checked_file_dma(std::string_view name, open_flags flags) noexcept {
|
|
return with_file_close_on_failure(open_file_dma(name, adjust_flags_for_integrity_checked_file(flags)), [name] (file f) {
|
|
return make_ready_future<file>(make_integrity_checked_file(name, f));
|
|
});
|
|
}
|
|
|
|
}
|