From 0ce1c525771d065db9f2e5b7bc9d51d7d3a64b88 Mon Sep 17 00:00:00 2001 From: Ollrogge Date: Wed, 8 Feb 2023 18:34:27 +0100 Subject: [PATCH] LibArchive: Add support for modification time and date --- Userland/Libraries/LibArchive/Zip.cpp | 10 ++++---- Userland/Libraries/LibArchive/Zip.h | 11 +++++---- Userland/Utilities/unzip.cpp | 33 +++++++++++++++++++++++++++ Userland/Utilities/zip.cpp | 13 +++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibArchive/Zip.cpp b/Userland/Libraries/LibArchive/Zip.cpp index 27c9a53707..535ebce403 100644 --- a/Userland/Libraries/LibArchive/Zip.cpp +++ b/Userland/Libraries/LibArchive/Zip.cpp @@ -90,6 +90,8 @@ ErrorOr Zip::for_each_member(Function 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 ZipOutputStream::add_member(ZipMember const& member) .minimum_version = minimum_version_needed(member.compression_method), .general_purpose_flags = { .flags = 0 }, .compression_method = static_cast(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(member.compressed_data.size()), .uncompressed_size = member.uncompressed_size, @@ -150,8 +152,8 @@ ErrorOr 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(member.compressed_data.size()), .uncompressed_size = member.uncompressed_size, diff --git a/Userland/Libraries/LibArchive/Zip.h b/Userland/Libraries/LibArchive/Zip.h index ad3c3bc758..2f9e8eedb3 100644 --- a/Userland/Libraries/LibArchive/Zip.h +++ b/Userland/Libraries/LibArchive/Zip.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -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 { diff --git a/Userland/Utilities/unzip.cpp b/Userland/Utilities/unzip.cpp index 293acf903c..3e9c9d3eea 100644 --- a/Userland/Utilities/unzip.cpp +++ b/Userland/Utilities/unzip.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -17,6 +18,18 @@ #include #include +static ErrorOr 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.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 serenity_main(Main::Arguments arguments) TRY(Core::System::chdir(output_directory_path)); } + Vector zip_directories; + auto success = TRY(zip_file->for_each_member([&](auto zip_member) { bool keep_file = false; @@ -142,10 +162,23 @@ ErrorOr 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; } diff --git a/Userland/Utilities/zip.cpp b/Userland/Utilities/zip.cpp index 3e44c26da4..44d71acef6 100644 --- a/Userland/Utilities/zip.cpp +++ b/Userland/Utilities/zip.cpp @@ -4,10 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include #include +#include #include #include #include @@ -58,6 +60,11 @@ ErrorOr 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 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);