:-'Generic Commit'
This commit is contained in:
parent
8556b683c7
commit
3bc0c65c40
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@ -83,6 +83,12 @@
|
||||
"variant": "cpp",
|
||||
"format": "cpp",
|
||||
"stdfloat": "cpp",
|
||||
"__nullptr": "cpp"
|
||||
"__nullptr": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"__hash_table": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"__tree": "cpp",
|
||||
"queue": "cpp",
|
||||
"stack": "cpp"
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
set(PROJECT_EXE_NAME dropshell-tool)
|
||||
project(${PROJECT_EXE_NAME} VERSION 1.0.0 LANGUAGES CXX)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
||||
# Force static linking globally
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
@ -9,7 +10,15 @@ set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries" FORCE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE OFF)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
||||
set(ZLIB_USE_STATIC_LIBS "ON")
|
||||
set(ZLIB_USE_STATIC_LIBS ON)
|
||||
|
||||
# Ensure zlib is built as static only
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(ZLIB_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(ZLIB_BUILD_STATIC ON CACHE BOOL "" FORCE)
|
||||
# Disable zlib examples and minigzip
|
||||
set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(ZLIB_BUILD_MINIGZIP OFF CACHE BOOL "" FORCE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
@ -86,18 +95,23 @@ FetchContent_MakeAvailable(cpptrace)
|
||||
FetchContent_Declare(
|
||||
nlohmann_json
|
||||
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
||||
GIT_TAG v3.11.3
|
||||
GIT_TAG v3.12.0
|
||||
)
|
||||
FetchContent_MakeAvailable(nlohmann_json)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(${PROJECT_EXE_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
# Fetch and build zlib from source (static)
|
||||
FetchContent_Declare(
|
||||
zlib
|
||||
URL https://zlib.net/zlib-1.3.1.tar.gz
|
||||
)
|
||||
FetchContent_MakeAvailable(zlib)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${PROJECT_EXE_NAME} PRIVATE
|
||||
libassert::assert
|
||||
cpptrace::cpptrace
|
||||
nlohmann_json::nlohmann_json
|
||||
zlibstatic
|
||||
)
|
||||
|
||||
# Set static linking flags
|
||||
@ -109,3 +123,9 @@ find_package(OpenSSL REQUIRED)
|
||||
target_compile_definitions(${PROJECT_EXE_NAME} PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
target_link_libraries(${PROJECT_EXE_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
|
||||
# --- MUSL CROSS-COMPILATION ---
|
||||
# To ensure all dependencies (including zlib) are built with musl, set your toolchain file or:
|
||||
# set(CMAKE_C_COMPILER "/path/to/musl-gcc")
|
||||
# set(CMAKE_CXX_COMPILER "/path/to/musl-g++")
|
||||
# BEFORE the project() call above.
|
||||
|
||||
|
@ -1,24 +1,212 @@
|
||||
#include "ArchiveManager.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cstring>
|
||||
#include <zlib.h>
|
||||
#include <sstream>
|
||||
#include <span>
|
||||
|
||||
#include "tar_to_stream.hpp"
|
||||
#include "ArchiveManager.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
constexpr size_t TAR_BLOCK_SIZE = 512;
|
||||
|
||||
// --- Minimal tar extraction logic for unpacking and config extraction ---
|
||||
struct TarHeader {
|
||||
char name[100];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char size[12];
|
||||
char mtime[12];
|
||||
char chksum[8];
|
||||
char typeflag;
|
||||
char linkname[100];
|
||||
char magic[6];
|
||||
char version[2];
|
||||
char uname[32];
|
||||
char gname[32];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char prefix[155];
|
||||
char pad[12];
|
||||
};
|
||||
|
||||
size_t tarFileSize(const TarHeader* hdr) {
|
||||
return std::strtol(hdr->size, nullptr, 8);
|
||||
}
|
||||
|
||||
std::string tarFileName(const TarHeader* hdr) {
|
||||
std::string name(hdr->name);
|
||||
if (hdr->prefix[0]) {
|
||||
std::string prefix(hdr->prefix);
|
||||
return prefix + "/" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool readTar(const std::vector<uint8_t>& tarData, const std::string& outDir, std::string* configJson = nullptr) {
|
||||
size_t pos = 0;
|
||||
while (pos + sizeof(TarHeader) <= tarData.size()) {
|
||||
const TarHeader* hdr = reinterpret_cast<const TarHeader*>(&tarData[pos]);
|
||||
if (hdr->name[0] == '\0') break;
|
||||
size_t filesize = tarFileSize(hdr);
|
||||
std::string filename = tarFileName(hdr);
|
||||
size_t fileStart = pos + sizeof(TarHeader);
|
||||
if (configJson && filename == "dropshell-tool-config.json") {
|
||||
*configJson = std::string(reinterpret_cast<const char*>(&tarData[fileStart]), filesize);
|
||||
} else if (!outDir.empty()) {
|
||||
fs::path outPath = fs::path(outDir) / filename;
|
||||
fs::create_directories(outPath.parent_path());
|
||||
std::ofstream ofs(outPath, std::ios::binary);
|
||||
ofs.write(reinterpret_cast<const char*>(&tarData[fileStart]), filesize);
|
||||
}
|
||||
pos = fileStart + filesize;
|
||||
pos += (TAR_BLOCK_SIZE - (filesize % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool extractConfigJson(const std::vector<uint8_t>& tarData, std::string& outJson) {
|
||||
return readTar(tarData, "", &outJson);
|
||||
}
|
||||
|
||||
bool replaceConfigJson(std::vector<uint8_t>& tarData, const std::string& json) {
|
||||
// Remove old config, add new one at end
|
||||
std::vector<uint8_t> newTar;
|
||||
size_t pos = 0;
|
||||
bool replaced = false;
|
||||
while (pos + sizeof(TarHeader) <= tarData.size()) {
|
||||
const TarHeader* hdr = reinterpret_cast<const TarHeader*>(&tarData[pos]);
|
||||
if (hdr->name[0] == '\0') break;
|
||||
size_t filesize = tarFileSize(hdr);
|
||||
std::string filename = tarFileName(hdr);
|
||||
size_t fileStart = pos + sizeof(TarHeader);
|
||||
if (filename != "dropshell-tool-config.json") {
|
||||
newTar.insert(newTar.end(), &tarData[pos], &tarData[fileStart + filesize]);
|
||||
size_t pad = (TAR_BLOCK_SIZE - (filesize % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE;
|
||||
newTar.insert(newTar.end(), pad, 0);
|
||||
} else {
|
||||
replaced = true;
|
||||
}
|
||||
pos = fileStart + filesize;
|
||||
pos += (TAR_BLOCK_SIZE - (filesize % TAR_BLOCK_SIZE)) % TAR_BLOCK_SIZE;
|
||||
}
|
||||
// Add new config
|
||||
// Use tar_to_stream to add the config file
|
||||
std::ostringstream oss(std::ios::binary);
|
||||
oss.write(reinterpret_cast<const char*>(newTar.data()), newTar.size());
|
||||
std::string uname = "root";
|
||||
std::string gname = "root";
|
||||
tar_to_stream_properties props = {
|
||||
"dropshell-tool-config.json",
|
||||
std::as_bytes(std::span(json.data(), json.size())),
|
||||
0u, "644", 0u, 0u, uname, gname
|
||||
};
|
||||
tar_to_stream(oss, std::move(props));
|
||||
tar_to_stream_tail(oss);
|
||||
std::string tarStr = oss.str();
|
||||
tarData.assign(tarStr.begin(), tarStr.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- zlib helpers ---
|
||||
bool gzipCompress(const std::string& in, std::vector<uint8_t>& out) {
|
||||
z_stream strm{};
|
||||
deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
out.resize(compressBound(in.size()));
|
||||
strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(in.data()));
|
||||
strm.avail_in = in.size();
|
||||
strm.next_out = out.data();
|
||||
strm.avail_out = out.size();
|
||||
int ret = deflate(&strm, Z_FINISH);
|
||||
if (ret != Z_STREAM_END) { deflateEnd(&strm); return false; }
|
||||
out.resize(strm.total_out);
|
||||
deflateEnd(&strm);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gzipDecompress(const std::string& inPath, std::vector<uint8_t>& out) {
|
||||
std::ifstream ifs(inPath, std::ios::binary);
|
||||
if (!ifs) return false;
|
||||
ifs.seekg(0, std::ios::end);
|
||||
size_t insize = ifs.tellg();
|
||||
ifs.seekg(0, std::ios::beg);
|
||||
std::vector<uint8_t> inbuf(insize);
|
||||
ifs.read(reinterpret_cast<char*>(inbuf.data()), insize);
|
||||
z_stream strm{};
|
||||
inflateInit2(&strm, 15 + 16);
|
||||
out.resize(insize * 10); // crude guess
|
||||
strm.next_in = inbuf.data();
|
||||
strm.avail_in = inbuf.size();
|
||||
strm.next_out = out.data();
|
||||
strm.avail_out = out.size();
|
||||
int ret = inflate(&strm, Z_FINISH);
|
||||
if (ret != Z_STREAM_END) { inflateEnd(&strm); return false; }
|
||||
out.resize(strm.total_out);
|
||||
inflateEnd(&strm);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gzipDecompressToTar(const std::string& inPath, std::vector<uint8_t>& tarData) {
|
||||
return gzipDecompress(inPath, tarData);
|
||||
}
|
||||
|
||||
bool gzipCompressToFile(const std::string& tarData, const std::string& outPath) {
|
||||
std::vector<uint8_t> gz;
|
||||
if (!gzipCompress(tarData, gz)) return false;
|
||||
std::ofstream ofs(outPath, std::ios::binary);
|
||||
if (!ofs) return false;
|
||||
ofs.write(reinterpret_cast<const char*>(gz.data()), gz.size());
|
||||
return ofs.good();
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveManager::ArchiveManager() {}
|
||||
|
||||
bool ArchiveManager::pack(const std::string& folderPath, const std::string& archivePath) {
|
||||
// TODO: Implement packing logic
|
||||
return false;
|
||||
// Use tar_to_stream to create tar in memory
|
||||
std::ostringstream tarStream(std::ios::binary);
|
||||
for (auto& p : fs::recursive_directory_iterator(folderPath)) {
|
||||
if (!fs::is_regular_file(p)) continue;
|
||||
std::ifstream ifs(p.path(), std::ios::binary);
|
||||
if (!ifs) return false;
|
||||
std::vector<char> data((std::istreambuf_iterator<char>(ifs)), {});
|
||||
std::string uname = "root";
|
||||
std::string gname = "root";
|
||||
tar_to_stream_properties props = {
|
||||
fs::relative(p.path(), folderPath).generic_string(),
|
||||
std::as_bytes(std::span(data.data(), data.size())),
|
||||
0u, "644", 0u, 0u, uname, gname
|
||||
};
|
||||
tar_to_stream(tarStream, std::move(props));
|
||||
}
|
||||
tar_to_stream_tail(tarStream);
|
||||
std::string tarData = tarStream.str();
|
||||
return gzipCompressToFile(tarData, archivePath);
|
||||
}
|
||||
|
||||
bool ArchiveManager::unpack(const std::string& archivePath, const std::string& outDir) {
|
||||
// TODO: Implement unpacking logic
|
||||
return false;
|
||||
std::vector<uint8_t> tarData;
|
||||
if (!gzipDecompressToTar(archivePath, tarData)) return false;
|
||||
return readTar(tarData, outDir);
|
||||
}
|
||||
|
||||
bool ArchiveManager::readConfigJson(const std::string& archivePath, std::string& outJson) {
|
||||
// TODO: Implement config extraction logic
|
||||
return false;
|
||||
std::vector<uint8_t> tarData;
|
||||
if (!gzipDecompressToTar(archivePath, tarData)) return false;
|
||||
return extractConfigJson(tarData, outJson);
|
||||
}
|
||||
|
||||
bool ArchiveManager::writeConfigJson(const std::string& archivePath, const std::string& json) {
|
||||
// TODO: Implement config writing logic
|
||||
return false;
|
||||
std::vector<uint8_t> tarData;
|
||||
if (!gzipDecompressToTar(archivePath, tarData)) return false;
|
||||
if (!replaceConfigJson(tarData, json)) return false;
|
||||
// Write back to archivePath
|
||||
std::string tarStr(reinterpret_cast<const char*>(tarData.data()), tarData.size());
|
||||
return gzipCompressToFile(tarStr, archivePath);
|
||||
}
|
@ -173,7 +173,9 @@ int publish_tool(int argc, char* argv[]) {
|
||||
std::cerr << "dropshell-tool-config.json not found in " << folder << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::filesystem::path archivePath = std::filesystem::path(folder) / (labeltag + ".tgz");
|
||||
std::filesystem::path archivePath = std::filesystem::path(home) / ".tmp" / (labeltag + ".tgz");
|
||||
std::filesystem::create_directories(archivePath.parent_path());
|
||||
|
||||
ArchiveManager archiver;
|
||||
if (!archiver.pack(folder, archivePath.string())) {
|
||||
std::cerr << "Failed to create archive." << std::endl;
|
||||
|
100
dropshell-tool/src/tar_to_stream.hpp
Normal file
100
dropshell-tool/src/tar_to_stream.hpp
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
struct tar_to_stream_properties {
|
||||
/// Properties of the file to enter into the stream
|
||||
std::string const &filename; /// name of the file to write
|
||||
std::span<std::byte const> data; /// the location of the file's contents in memory
|
||||
uint64_t mtime{0u}; /// file modification time, in seconds since epoch
|
||||
std::string filemode{"644"}; /// file mode
|
||||
unsigned int uid{0u}; /// file owner user ID
|
||||
unsigned int gid{0u}; /// file owner group ID
|
||||
std::string const &uname{"root"}; /// file owner username
|
||||
std::string const &gname{"root"}; /// file owner group name
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void tar_to_stream(T &stream, /// stream to write to, e.g. ostream or ofstream
|
||||
tar_to_stream_properties &&file) { /// properties of the file to enter into the stream
|
||||
/// Read a "file" in memory, and write it as a TAR archive to the stream
|
||||
struct { // offset
|
||||
char name[100]{}; // 0 filename
|
||||
char mode[8]{}; // 100 file mode: 0000644 etc
|
||||
char uid[8]{}; // 108 user id, ascii representation of octal value: "0001750" (for UID 1000)
|
||||
char gid[8]{}; // 116 group id, ascii representation of octal value: "0001750" (for GID 1000)
|
||||
char size[12]{}; // 124 file size, ascii representation of octal value
|
||||
char mtime[12]{"00000000000"}; // 136 modification time, seconds since epoch
|
||||
char chksum[8]{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; // 148 checksum: six octal bytes followed by null and ' '. Checksum is the octal sum of all bytes in the header, with chksum field set to 8 spaces.
|
||||
char typeflag{'0'}; // 156 '0'
|
||||
char linkname[100]{}; // 157 null bytes when not a link
|
||||
char magic[6]{'u', 's', 't', 'a', 'r', ' '}; // 257 format: Unix Standard TAR: "ustar ", not null-terminated
|
||||
char version[2]{" "}; // 263 " "
|
||||
char uname[32]{}; // 265 user name
|
||||
char gname[32]{}; // 297 group name
|
||||
char devmajor[8]{}; // 329 null bytes
|
||||
char devminor[8]{}; // 337 null bytes
|
||||
char prefix[155]{}; // 345 null bytes
|
||||
char padding[12]{}; // 500 padding to reach 512 block size
|
||||
} header; // 512
|
||||
|
||||
file.filemode.insert(file.filemode.begin(), 7 - file.filemode.length(), '0'); // zero-pad the file mode
|
||||
|
||||
std::strncpy(header.name, file.filename.c_str(), sizeof(header.name ) - 1); // leave one char for the final null
|
||||
std::strncpy(header.mode, file.filemode.c_str(), sizeof(header.mode ) - 1);
|
||||
std::strncpy(header.uname, file.uname.c_str(), sizeof(header.uname) - 1);
|
||||
std::strncpy(header.gname, file.gname.c_str(), sizeof(header.gname) - 1);
|
||||
|
||||
sprintf(header.size, "%011lo", file.data.size());
|
||||
sprintf(header.mtime, "%011llo", file.mtime);
|
||||
sprintf(header.uid, "%07o", file.uid);
|
||||
sprintf(header.gid, "%07o", file.gid);
|
||||
|
||||
{
|
||||
unsigned int checksum_value = 0;
|
||||
for(size_t i{0}; i != sizeof(header); ++i) {
|
||||
checksum_value += reinterpret_cast<uint8_t*>(&header)[i];
|
||||
}
|
||||
sprintf(header.chksum, "%06o", checksum_value);
|
||||
}
|
||||
|
||||
size_t const padding{(512u - file.data.size() % 512) & 511u};
|
||||
stream << std::string_view{header.name, sizeof(header)}
|
||||
<< std::string_view{reinterpret_cast<char const*>(file.data.data()), file.data.size()}
|
||||
<< std::string(padding, '\0');
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[deprecated("Use tar_to_stream_properties as argument: tar_to_stream(stream, {...}) - this allows use of designated initialisers and cleaner code. Refer to tar_to_stream's README for example usage")]]
|
||||
void tar_to_stream(T &stream, /// stream to write to, e.g. ostream or ofstream
|
||||
std::string const &filename, /// name of the file to write
|
||||
char const *data_ptr, /// pointer to the data in this archive segment
|
||||
size_t data_size, /// size of the data
|
||||
uint64_t mtime = 0u, /// file modification time, in seconds since epoch
|
||||
std::string filemode = "644", /// file mode
|
||||
unsigned int uid = 0u, /// file owner user ID
|
||||
unsigned int gid = 0u, /// file owner group ID
|
||||
std::string const &uname = "root", /// file owner username
|
||||
std::string const &gname = "root") { /// file owner group name
|
||||
/// Explicit argument constructor, for backwards compatibility
|
||||
tar_to_stream(stream, tar_to_stream_properties{
|
||||
.filename{filename},
|
||||
.data{std::as_bytes(std::span{data_ptr, data_size})},
|
||||
.mtime{mtime},
|
||||
.filemode{filemode},
|
||||
.uid{uid},
|
||||
.gid{gid},
|
||||
.uname{uname},
|
||||
.gname{gname},
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void tar_to_stream_tail(T &stream, unsigned int tail_length = 512u * 2u) {
|
||||
/// TAR archives expect a tail of null bytes at the end - min of 512 * 2, but implementations often add more
|
||||
stream << std::string(tail_length, '\0');
|
||||
}
|
BIN
dropshell-tool/tool/dropshell-tool
Executable file
BIN
dropshell-tool/tool/dropshell-tool
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user