LibArchive: Add support for modification time and date

This commit is contained in:
Ollrogge 2023-02-08 18:34:27 +01:00 committed by Andrew Kaster
parent 361df6eff8
commit 0ce1c52577
Notes: sideshowbarker 2024-07-17 10:05:47 +09:00
4 changed files with 59 additions and 8 deletions

View File

@ -90,6 +90,8 @@ ErrorOr<bool> Zip::for_each_member(Function<IterationDecision(ZipMember const&)>
member.compression_method = central_directory_record.compression_method;
member.uncompressed_size = central_directory_record.uncompressed_size;
member.crc32 = central_directory_record.crc32;
member.modification_time = central_directory_record.modification_time;
member.modification_date = central_directory_record.modification_date;
member.is_directory = central_directory_record.external_attributes & zip_directory_external_attribute || member.name.bytes_as_string_view().ends_with('/'); // FIXME: better directory detection
if (callback(member) == IterationDecision::Break)
@ -122,8 +124,8 @@ ErrorOr<void> ZipOutputStream::add_member(ZipMember const& member)
.minimum_version = minimum_version_needed(member.compression_method),
.general_purpose_flags = { .flags = 0 },
.compression_method = static_cast<u16>(member.compression_method),
.modification_time = 0, // TODO: support modification time
.modification_date = 0,
.modification_time = member.modification_time,
.modification_date = member.modification_date,
.crc32 = member.crc32,
.compressed_size = static_cast<u32>(member.compressed_data.size()),
.uncompressed_size = member.uncompressed_size,
@ -150,8 +152,8 @@ ErrorOr<void> ZipOutputStream::finish()
.minimum_version = zip_version,
.general_purpose_flags = { .flags = 0 },
.compression_method = member.compression_method,
.modification_time = 0, // TODO: support modification time
.modification_date = 0,
.modification_time = member.modification_time,
.modification_date = member.modification_date,
.crc32 = member.crc32,
.compressed_size = static_cast<u32>(member.compressed_data.size()),
.uncompressed_size = member.uncompressed_size,

View File

@ -8,6 +8,7 @@
#pragma once
#include <AK/Array.h>
#include <AK/DOSPackedTime.h>
#include <AK/Function.h>
#include <AK/IterationDecision.h>
#include <AK/String.h>
@ -112,8 +113,8 @@ struct [[gnu::packed]] CentralDirectoryRecord {
u16 minimum_version;
ZipGeneralPurposeFlags general_purpose_flags;
ZipCompressionMethod compression_method;
u16 modification_time;
u16 modification_date;
DOSPackedTime modification_time;
DOSPackedDate modification_date;
u32 crc32;
u32 compressed_size;
u32 uncompressed_size;
@ -186,8 +187,8 @@ struct [[gnu::packed]] LocalFileHeader {
u16 minimum_version;
ZipGeneralPurposeFlags general_purpose_flags;
u16 compression_method;
u16 modification_time;
u16 modification_date;
DOSPackedTime modification_time;
DOSPackedDate modification_date;
u32 crc32;
u32 compressed_size;
u32 uncompressed_size;
@ -244,6 +245,8 @@ struct ZipMember {
u32 uncompressed_size;
u32 crc32;
bool is_directory;
DOSPackedTime modification_time;
DOSPackedDate modification_date;
};
class Zip {

View File

@ -5,6 +5,7 @@
*/
#include <AK/Assertions.h>
#include <AK/DOSPackedTime.h>
#include <AK/NumberFormat.h>
#include <AK/StringUtils.h>
#include <LibArchive/Zip.h>
@ -17,6 +18,18 @@
#include <LibCrypto/Checksum/CRC32.h>
#include <sys/stat.h>
static ErrorOr<void> adjust_modification_time(Archive::ZipMember const& zip_member)
{
auto time = time_from_packed_dos(zip_member.modification_date, zip_member.modification_time);
auto seconds = static_cast<time_t>(time.to_seconds());
struct utimbuf buf {
.actime = seconds,
.modtime = seconds
};
return Core::System::utime(zip_member.name, buf);
}
static bool unpack_zip_member(Archive::ZipMember zip_member, bool quiet)
{
if (zip_member.is_directory) {
@ -69,6 +82,11 @@ static bool unpack_zip_member(Archive::ZipMember zip_member, bool quiet)
VERIFY_NOT_REACHED();
}
if (adjust_modification_time(zip_member).is_error()) {
warnln("Failed setting modification_time for file {}", zip_member.name);
return false;
}
if (!new_file->close()) {
warnln("Can't close file {}: {}", zip_member.name, new_file->error_string());
return false;
@ -123,6 +141,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
TRY(Core::System::chdir(output_directory_path));
}
Vector<Archive::ZipMember> zip_directories;
auto success = TRY(zip_file->for_each_member([&](auto zip_member) {
bool keep_file = false;
@ -142,10 +162,23 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
if (keep_file) {
if (!unpack_zip_member(zip_member, quiet))
return IterationDecision::Break;
if (zip_member.is_directory)
zip_directories.append(zip_member);
}
return IterationDecision::Continue;
}));
if (!success) {
return 1;
}
for (auto& directory : zip_directories) {
if (adjust_modification_time(directory).is_error()) {
warnln("Failed setting modification time for directory {}", directory.name);
return 1;
}
}
return success ? 0 : 1;
}

View File

@ -4,10 +4,12 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/DOSPackedTime.h>
#include <AK/LexicalPath.h>
#include <LibArchive/Zip.h>
#include <LibCompress/Deflate.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibCore/Stream.h>
@ -58,6 +60,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Archive::ZipMember member {};
member.name = TRY(String::from_deprecated_string(canonicalized_path));
auto stat = TRY(Core::System::fstat(file->fd()));
auto date = Core::DateTime::from_timestamp(stat.st_mtim.tv_sec);
member.modification_date = to_packed_dos_date(date.year(), date.month(), date.day());
member.modification_time = to_packed_dos_time(date.hour(), date.minute(), date.second());
auto deflate_buffer = Compress::DeflateCompressor::compress_all(file_buffer);
if (!deflate_buffer.is_error() && deflate_buffer.value().size() < file_buffer.size()) {
member.compressed_data = deflate_buffer.value().bytes();
@ -85,6 +92,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
member.uncompressed_size = 0;
member.crc32 = 0;
member.is_directory = true;
auto stat = TRY(Core::System::stat(canonicalized_path));
auto date = Core::DateTime::from_timestamp(stat.st_mtim.tv_sec);
member.modification_date = to_packed_dos_date(date.year(), date.month(), date.day());
member.modification_time = to_packed_dos_time(date.hour(), date.minute(), date.second());
TRY(zip_stream.add_member(member));
outln(" adding: {} (stored 0%)", canonicalized_path);