@@ -94,7 +94,10 @@ ErrorOr<bool> Zip::for_each_member(Function<ErrorOr<IterationDecision>(ZipMember
9494        member.crc32  = central_directory_record.crc32 ;
9595        member.modification_time  = central_directory_record.modification_time ;
9696        member.modification_date  = central_directory_record.modification_date ;
97-         member.is_directory  = central_directory_record.external_attributes  & zip_directory_external_attribute || member.name .bytes_as_string_view ().ends_with (' /' //  FIXME: better directory detection
97+         member.is_directory  = central_directory_record.external_attributes .msdos  & zip_directory_msdos_attribute || member.name .bytes_as_string_view ().ends_with (' /' //  FIXME: better directory detection
98+         if  (central_directory_record.made_by_version .made_by  == zip_made_by_unix) {
99+             member.mode  = static_cast <mode_t >(central_directory_record.external_attributes .unix );
100+         }
98101
99102        if  (TRY (callback (member)) == IterationDecision::Break)
100103            return  false ;
@@ -158,7 +161,7 @@ ErrorOr<void> ZipOutputStream::add_member(ZipMember const& member)
158161    return  local_file_header.write (*m_stream);
159162}
160163
161- ErrorOr<ZipOutputStream::MemberInformation> ZipOutputStream::add_member_from_stream (StringView path, Stream& stream, Optional<Core::DateTime> const & modification_time)
164+ ErrorOr<ZipOutputStream::MemberInformation> ZipOutputStream::add_member_from_stream (StringView path, Stream& stream, Optional<Core::DateTime> const & modification_time, Optional< mode_t > mode )
162165{
163166    auto  buffer = TRY (stream.read_until_eof ());
164167
@@ -190,13 +193,14 @@ ErrorOr<ZipOutputStream::MemberInformation> ZipOutputStream::add_member_from_str
190193    Crypto::Checksum::CRC32 checksum { buffer.bytes () };
191194    member.crc32  = checksum.digest ();
192195    member.is_directory  = false ;
196+     member.mode  = mode;
193197
194198    TRY (add_member (member));
195199
196200    return  MemberInformation { compression_ratio, compressed_size };
197201}
198202
199- ErrorOr<void > ZipOutputStream::add_directory (StringView name, Optional<Core::DateTime> const & modification_time)
203+ ErrorOr<void > ZipOutputStream::add_directory (StringView name, Optional<Core::DateTime> const & modification_time, Optional< mode_t > mode )
200204{
201205    Archive::ZipMember member {};
202206    member.name  = TRY (String::from_utf8 (name));
@@ -205,6 +209,7 @@ ErrorOr<void> ZipOutputStream::add_directory(StringView name, Optional<Core::Dat
205209    member.uncompressed_size  = 0 ;
206210    member.crc32  = 0 ;
207211    member.is_directory  = true ;
212+     member.mode  = mode;
208213
209214    if  (modification_time.has_value ()) {
210215        member.modification_date  = to_packed_dos_date (modification_time->year (), modification_time->month (), modification_time->day ());
@@ -223,8 +228,12 @@ ErrorOr<void> ZipOutputStream::finish()
223228    auto  central_directory_size = 0u ;
224229    for  (ZipMember const & member : m_members) {
225230        auto  zip_version = minimum_version_needed (member.compression_method );
231+         ZipExternalAttributes external_attributes = {
232+             .msdos  = static_cast <u16 >(member.is_directory  ? zip_directory_msdos_attribute : 0 ),
233+             .unix  = member.mode .value_or (0 ),
234+         };
226235        CentralDirectoryRecord central_directory_record {
227-             .made_by_version  = zip_version,
236+             .made_by_version  = { . version  =  static_cast < u16 >( zip_version), . made_by  = zip_made_by_unix } ,
228237            .minimum_version  = zip_version,
229238            .general_purpose_flags  = { .flags  = 0  },
230239            .compression_method  = member.compression_method ,
@@ -238,7 +247,7 @@ ErrorOr<void> ZipOutputStream::finish()
238247            .comment_length  = 0 ,
239248            .start_disk  = 0 ,
240249            .internal_attributes  = 0 ,
241-             .external_attributes  = member. is_directory  ? zip_directory_external_attribute :  0 ,
250+             .external_attributes  = external_attributes ,
242251            .local_file_header_offset  = file_header_offset, //  FIXME: we assume the wrapped output stream was never written to before us
243252            .name  = reinterpret_cast <u8  const *>(member.name .bytes_as_string_view ().characters_without_null_termination ()),
244253            .extra_data  = nullptr ,
0 commit comments