Repository: bitcoin/bitcoin
File: src/streams.cpp (C++)

Code before:
// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.

#include <span.h>
#include <streams.h>

#include <array>

std::size_t AutoFile::detail_fread(Span<std::byte> dst)
{
    if (!m_file) throw std::ios_base::failure("AutoFile::read: file handle is nullptr");
    if (m_xor.empty()) {
        return std::fread(dst.data(), 1, dst.size(), m_file);
    } else {
        const auto init_pos{std::ftell(m_file)};
        if (init_pos < 0) throw std::ios_base::failure("AutoFile::read: ftell failed");
        std::size_t ret{std::fread(dst.data(), 1, dst.size(), m_file)};
        util::Xor(dst.subspan(0, ret), m_xor, init_pos);
        return ret;
    }
}

void AutoFile::seek(int64_t offset, int origin)
{
    if (IsNull()) {
        throw std::ios_base::failure("AutoFile::seek: file handle is nullptr");
    }
    if (std::fseek(m_file, offset, origin) != 0) {
        throw std::ios_base::failure(feof() ? "AutoFile::seek: end of file" : "AutoFile::seek: fseek failed");
    }
}

int64_t AutoFile::tell()
{
    if (IsNull()) {
        throw std::ios_base::failure("AutoFile::tell: file handle is nullptr");
    }
    int64_t r{std::ftell(m_file)};
    if (r < 0) {
        throw std::ios_base::failure("AutoFile::tell: ftell failed");
    }
    return r;
}

void AutoFile::read(Span<std::byte> dst)
{
    if (detail_fread(dst) != dst.size()) {

Review comment (bug):
Outside of the scope of this PR: I feel like this should probably handle a failed `fread` explicitly, even though the subspan call below means XOR won't do anything bad here, and when this returns to `AutoFile::read()` or `BufferedFile::fill()` (it's only two callers) the error gets handled. It seems kind of footgun-like to let this potentially dangerous file pointer get passed around and I'm not sure it's worth `BufferedFile` being able to print it's own name when throwing an exception.

Code after:
// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.

#include <span.h>
#include <streams.h>
#include <util/fs_helpers.h>

#include <array>

AutoFile::AutoFile(std::FILE* file, std::vector<std::byte> data_xor)
    : m_file{file}, m_xor{std::move(data_xor)}
{
    if (!IsNull()) {
        auto pos{std::ftell(m_file)};
        if (pos >= 0) m_position = pos;
    }
}

std::size_t AutoFile::detail_fread(Span<std::byte> dst)
{
    if (!m_file) throw std::ios_base::failure("AutoFile::read: file handle is nullptr");
    size_t ret = std::fread(dst.data(), 1, dst.size(), m_file);
    if (!m_xor.empty()) {
        if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::read: position unknown");
        util::Xor(dst.subspan(0, ret), m_xor, *m_position);
    }
    if (m_position.has_value()) *m_position += ret;
    return ret;
}

void AutoFile::seek(int64_t offset, int origin)
{
    if (IsNull()) {
        throw std::ios_base::failure("AutoFile::seek: file handle is nullptr");
    }
    if (std::fseek(m_file, offset, origin) != 0) {
        throw std::ios_base::failure(feof() ? "AutoFile::seek: end of file" : "AutoFile::seek: fseek failed");
    }
    if (origin == SEEK_SET) {
        m_position = offset;
    } else if (origin == SEEK_CUR && m_position.has_value()) {
        *m_position += offset;
    } else {
        int64_t r{std::ftell(m_file)};
        if (r < 0) {
            throw std::ios_base::failure("AutoFile::seek: ftell failed");
        }