Refactored namespaces

This commit is contained in:
emeric
2024-03-12 08:32:08 +01:00
parent 487960b413
commit 4b7c4295ec
501 changed files with 12605 additions and 12631 deletions
+298
View File
@@ -0,0 +1,298 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ArchiveZipper.hpp"
#include <algorithm>
#include <cassert>
#include <cstring> // strerror
#include <fstream>
#include <archive.h>
#include <archive_entry.h>
#include "core/ILogger.hpp"
namespace lms::zip
{
std::unique_ptr<IZipper> createArchiveZipper(const EntryContainer& entries)
{
return std::make_unique<ArchiveZipper>(entries);
}
class FileException : public Exception
{
public:
FileException(const std::filesystem::path& p, std::string_view message)
: Exception{ "File '" + p.string() + "': " + std::string {message} }
{}
FileException(const std::filesystem::path& p, std::string_view message, int err)
: Exception{ "File '" + p.string() + "': " + std::string {message} + ": " + ::strerror(err) }
{}
};
class ArchiveException : public Exception
{
public:
ArchiveException(struct ::archive* arch)
: Exception{ getError(arch) }
{}
static std::string_view getError(struct ::archive* arch)
{
const char* str{ archive_error_string(arch) };
if (!str)
{
static std::string unknownError{ "Unknown archive error" };
return unknownError;
}
return str;
}
};
void ArchiveZipper::ArchiveDeleter::operator()(struct ::archive* arch)
{
const int res{ ::archive_write_free(arch) };
if (res != ARCHIVE_OK)
LMS_LOG(UTILS, ERROR, "Failure while freeing archive control struct: " << std::string{ ::strerror(res) });
}
void ArchiveZipper::ArchiveEntryDeleter::operator()(struct ::archive_entry* archEntry)
{
::archive_entry_free(archEntry);
}
ArchiveZipper::ArchiveZipper(const EntryContainer& entries)
: _entries{ entries }
, _readBuffer(_readBufferSize, {})
, _currentEntry{ std::cbegin(_entries) }
{
_archive = ArchivePtr{ ::archive_write_new() };
if (!_archive)
throw Exception{ "Cannot create archive control struct" };
auto archiveOpen{ [](struct ::archive*, void*)
{
return ARCHIVE_OK;
} };
auto archiveWrite{ [](struct ::archive*, void* clientData, const void* buff, ::size_t n) -> la_ssize_t
{
ArchiveZipper* zipper {static_cast<ArchiveZipper*>(clientData)};
return zipper->onWriteCallback(static_cast<const std::byte*>(buff), n);
} };
auto archiveClose{ [](struct ::archive*, void*)
{
return ARCHIVE_OK;
} };
if (::archive_write_set_bytes_per_block(_archive.get(), _writeBlockSize) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
// 1 => no padding for last block
if (::archive_write_set_bytes_in_last_block(_archive.get(), 1) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
if (::archive_write_set_format_zip(_archive.get()) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
if (::archive_write_set_option(_archive.get(), "zip", "compression", "deflate") != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
int res{ ::archive_write_open(_archive.get(), this, archiveOpen, archiveWrite, archiveClose) };
if (res != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
}
std::uint64_t ArchiveZipper::writeSome(std::ostream& output)
{
assert(!_currentOutputStream);
_currentOutputStream = &output;
_bytesWrittenInCurrentOutputStream = 0;
while (_bytesWrittenInCurrentOutputStream == 0)
{
if (!_currentArchiveEntry)
{
if (_currentEntry == std::cend(_entries))
{
if (::archive_write_close(_archive.get()) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
_archive.reset();
break;
}
_currentArchiveEntry = createArchiveEntry(*_currentEntry);
_currentEntryOffset = 0;
if (::archive_write_header(_archive.get(), _currentArchiveEntry.get()) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
}
if (writeSomeCurrentFileData())
{
// entry complete
if (::archive_write_finish_entry(_archive.get()) != ARCHIVE_OK)
throw ArchiveException{ _archive.get() };
_currentArchiveEntry.reset();
_currentEntry++;
}
}
_currentOutputStream = nullptr;
return _bytesWrittenInCurrentOutputStream;
}
bool ArchiveZipper::isComplete() const
{
return !_archive;
}
void ArchiveZipper::abort()
{
LMS_LOG(UTILS, DEBUG, "Aborting zip creation");
if (_archive)
{
::archive_write_fail(_archive.get());
_archive.reset();
}
}
static ::mode_t permsToMode(const std::filesystem::perms p)
{
using std::filesystem::perms;
::mode_t mode{};
auto testPerm{ [](perms p, perms permToTest)
{
return (p & permToTest) == permToTest;
} };
if (testPerm(p, perms::owner_read))
mode |= S_IRUSR;
if (testPerm(p, perms::owner_write))
mode |= S_IWUSR;
if (testPerm(p, perms::owner_exec))
mode |= S_IXUSR;
if (testPerm(p, perms::group_read))
mode |= S_IRGRP;
if (testPerm(p, perms::group_write))
mode |= S_IWGRP;
if (testPerm(p, perms::group_exec))
mode |= S_IXGRP;
if (testPerm(p, perms::others_read))
mode |= S_IROTH;
if (testPerm(p, perms::others_write))
mode |= S_IWOTH;
if (testPerm(p, perms::others_exec))
mode |= S_IXOTH;
return mode;
}
ArchiveZipper::ArchiveEntryPtr ArchiveZipper::createArchiveEntry(const Entry& entry)
{
try
{
if (!std::filesystem::is_regular_file(entry.filePath))
throw FileException{ entry.filePath, "not a regular file" };
ArchiveEntryPtr archiveEntry{ archive_entry_new() };
if (!archiveEntry)
throw Exception{ "Cannot create archive entry control struct" };
archive_entry_set_pathname(archiveEntry.get(), entry.fileName.c_str());
archive_entry_set_size(archiveEntry.get(), std::filesystem::file_size(entry.filePath));
archive_entry_set_mode(archiveEntry.get(), permsToMode(std::filesystem::status(entry.filePath).permissions()));
archive_entry_set_filetype(archiveEntry.get(), AE_IFREG);
return archiveEntry;
}
catch (const std::filesystem::filesystem_error& error)
{
throw FileException{ entry.filePath, error.what() };
}
}
bool ArchiveZipper::writeSomeCurrentFileData()
{
assert(_currentEntry != std::cend(_entries));
std::ifstream ifs{ _currentEntry->filePath.c_str(), std::ios_base::binary };
if (!ifs)
throw FileException{ _currentEntry->filePath, "cannot open file", errno };
ifs.seekg(0, std::ios::end);
const std::uint64_t fileSize{ static_cast<std::uint64_t>(ifs.tellg()) };
ifs.seekg(0, std::ios::beg);
// TODO: store file size?
if (fileSize < _currentEntryOffset)
throw FileException{ _currentEntry->filePath, "size changed?" };
const std::uint64_t bytesToRead{ std::min(fileSize - _currentEntryOffset, static_cast<std::uint64_t>(_readBufferSize)) };
// read from file
if (!ifs.seekg(_currentEntryOffset, std::ios::beg))
throw FileException{ _currentEntry->filePath, "seek failed", errno };
if (!ifs.read(reinterpret_cast<char*>(&_readBuffer[0]), bytesToRead))
throw FileException{ _currentEntry->filePath, "read failed", errno };
const std::uint64_t actualBytesRead{ static_cast<std::uint64_t>(ifs.gcount()) };
// write to archive
{
std::uint64_t remainingBytesToWrite{ actualBytesRead };
while (remainingBytesToWrite > 0)
{
const auto writtenBytes{ archive_write_data(_archive.get(), &_readBuffer[actualBytesRead - remainingBytesToWrite], remainingBytesToWrite) };
if (writtenBytes < 0)
throw ArchiveException{ _archive.get() };
assert(static_cast<std::uint64_t>(writtenBytes) <= remainingBytesToWrite);
remainingBytesToWrite -= writtenBytes;
}
}
_currentEntryOffset += actualBytesRead;
return (_currentEntryOffset >= fileSize);
}
std::int64_t ArchiveZipper::onWriteCallback(const std::byte* buffer, std::size_t bufferSize)
{
if (!_currentOutputStream)
{
archive_set_error(_archive.get(), EIO, "IO error: operation cancelled");
return -1;
}
_currentOutputStream->write(reinterpret_cast<const char*>(buffer), bufferSize);
if (!*_currentOutputStream)
throw Exception{ "Failed to write " + std::to_string(bufferSize) + " bytes in final archive output!" };
_bytesWrittenInCurrentOutputStream += bufferSize;
return bufferSize;
}
}