Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.11.7
v0.11.8
17 changes: 12 additions & 5 deletions src/commands/spratlayout_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
#include <fcntl.h>
#include <stdio.h>
#include <intrin.h>
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef _O_BINARY
#define _O_BINARY 0x8000
#endif
Expand Down Expand Up @@ -1085,8 +1082,18 @@ bool extract_tar_from_stdin(const fs::path& output_dir) {
archive_read_support_format_all(a);
archive_read_support_filter_all(a);

// Open from stdin
if (archive_read_open_fd(a, STDIN_FILENO, k_tar_read_buffer_size) != ARCHIVE_OK) {
// Use a read callback so that input flows through std::cin. In embedded
// mode std::cin is redirected to a stringstream; reading from the OS-level
// stdin fd would bypass that redirection and receive no data.
auto tar_read_cb = [](struct archive* /*unused*/, void* /*client_data*/,
const void** buffer) -> la_ssize_t {
static thread_local std::vector<char> buf(k_tar_read_buffer_size);
std::cin.read(buf.data(), static_cast<std::streamsize>(buf.size()));
auto n = std::cin.gcount();
*buffer = buf.data();
return static_cast<la_ssize_t>(n);
};
Comment on lines +1088 to +1095

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a read error occurs on std::cin, the callback currently returns 0 (via std::cin.gcount()), which libarchive interprets as a clean End-Of-File (EOF). To prevent silent failures or truncated extractions on actual I/O errors, we should check std::cin.bad() or std::cin.fail() and report the error to libarchive using archive_set_error before returning -1.

    auto tar_read_cb = [](struct archive* a, void* /*client_data*/,
                          const void** buffer) -> la_ssize_t {
        static thread_local std::vector<char> buf(k_tar_read_buffer_size);
        std::cin.read(buf.data(), static_cast<std::streamsize>(buf.size()));
        if (std::cin.bad() || (std::cin.fail() && !std::cin.eof())) {
            archive_set_error(a, EIO, "std::cin read error");
            return -1;
        }
        auto n = std::cin.gcount();
        *buffer = buf.data();
        return static_cast<la_ssize_t>(n);
    };

if (archive_read_open(a, nullptr, nullptr, tar_read_cb, nullptr) != ARCHIVE_OK) {
std::cerr << tr("Error: Failed to open stdin for archive extraction: ") << archive_error_string(a) << '\n';
archive_read_free(a);
return false;
Expand Down
25 changes: 16 additions & 9 deletions src/commands/spratpack_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,12 +640,21 @@ int run_spratpack(int argc, char** argv) {
a.reset(archive_write_new());
archive_write_set_format_pax_restricted(a.get());
#ifdef _WIN32
if (_setmode(_fileno(stdout), _O_BINARY) == -1) {
std::cerr << tr("Failed to set stdout to binary mode\n");
return 1;
}
// Non-fatal: in embedded mode stdout may not be a real file handle.
_setmode(_fileno(stdout), _O_BINARY);
#endif
if (archive_write_open_FILE(a.get(), stdout) != ARCHIVE_OK) {
// Use a write callback instead of archive_write_open_FILE so that
// output flows through std::cout. In embedded mode std::cout is
// redirected to a stringstream; writing to the C-level stdout FILE*
// would bypass that redirection and lose all data.
auto tar_write_cb = [](struct archive* /*unused*/, void* /*client_data*/,
const void* buffer, size_t length) -> la_ssize_t {
std::cout.write(static_cast<const char*>(buffer),
static_cast<std::streamsize>(length));
if (std::cout.fail()) return -1;
return static_cast<la_ssize_t>(length);
};
Comment on lines +650 to +656

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a write error occurs on std::cout, the callback returns -1 but does not set an error on the archive object. To ensure that libarchive can report a meaningful error message (e.g., via archive_error_string), we should call archive_set_error with a proper error code (like EIO) before returning -1.

        auto tar_write_cb = [](struct archive* a, void* /*client_data*/,
                               const void* buffer, size_t length) -> la_ssize_t {
            std::cout.write(static_cast<const char*>(buffer),
                            static_cast<std::streamsize>(length));
            if (std::cout.fail()) {
                archive_set_error(a, EIO, "std::cout write error");
                return -1;
            }
            return static_cast<la_ssize_t>(length);
        };

if (archive_write_open(a.get(), nullptr, nullptr, tar_write_cb, nullptr) != ARCHIVE_OK) {
std::cerr << tr("Failed to open TAR stream on stdout: ") << archive_error_string(a.get()) << "\n";
return 1;
}
Expand Down Expand Up @@ -947,10 +956,8 @@ int run_spratpack(int argc, char** argv) {
archive_entry_free(entry);
} else if (output_pattern.empty()) {
#ifdef _WIN32
if (_setmode(_fileno(stdout), _O_BINARY) == -1) {
std::cerr << tr("Failed to set stdout to binary mode\n");
return 1;
}
// Non-fatal: in embedded mode stdout may not be a real file handle.
_setmode(_fileno(stdout), _O_BINARY);
#endif
std::cout.write(reinterpret_cast<const char*>(output_data.data()), static_cast<std::streamsize>(output_data.size()));
} else {
Expand Down
Loading