-
Notifications
You must be signed in to change notification settings - Fork 110
fix: rewrite extra fields #879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Its-Just-Nans
wants to merge
122
commits into
master
Choose a base branch
from
rewrite-extra-fields
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 107 commits
Commits
Show all changes
122 commits
Select commit
Hold shift + click to select a range
6572884
add
Its-Just-Nans 5d2c43b
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans 15248f5
remove useless compression_method in settings
Its-Just-Nans b5f6d56
fmt
Its-Just-Nans a61a694
add
Its-Just-Nans 4ec7c11
push
Its-Just-Nans f35a6ec
Merge branch 'master' into rewrite-aes-settings
Its-Just-Nans 1f5aa77
fix
Its-Just-Nans 0c3ea80
fix
Its-Just-Nans 4e28a55
Merge branch 'rewrite-aes-settings' into rewrite-aes-settings-2
Its-Just-Nans 20c8e19
add
Its-Just-Nans 67b3de2
cleanup
Its-Just-Nans a0a9c95
revert
Its-Just-Nans e86d776
fix variable
Its-Just-Nans e38826f
better cleanup
Its-Just-Nans 2299a76
add comment
Its-Just-Nans 7800212
refactor: change the vendor version
Its-Just-Nans 66cdb81
allow for stream with AES
Its-Just-Nans 25d789b
Merge branch 'master' into rewrite-aes-settings-2
Its-Just-Nans 72b2a30
Merge branch 'rewrite-aes-settings-2' into rewrite-aes-settings-3
Its-Just-Nans 7363824
sync
Its-Just-Nans f466e9a
Merge branch 'rewrite-aes-settings-2' into rewrite-aes-settings-3
Its-Just-Nans c1cdccf
fix clippy
Its-Just-Nans 3d6f36b
Merge branch 'rewrite-aes-settings-2' into rewrite-aes-settings-3
Its-Just-Nans cf941b1
fmt
Its-Just-Nans ab1dc08
cleanup
Its-Just-Nans d7b8ce0
Merge branch 'master' into rewrite-aes-settings-3
Its-Just-Nans b357072
cargo clippy
Its-Just-Nans 74c5031
cleanup
Its-Just-Nans 7b13674
fix clippy
Its-Just-Nans 2ea512f
Merge branch 'rewrite-aes-settings-4' into rewrite-extra-fields
Its-Just-Nans f97b093
Merge branch 'master' into rewrite-aes-settings-4
Its-Just-Nans 6dc3243
add
Its-Just-Nans 95348bf
sync
Its-Just-Nans 37a16f7
Merge branch 'rewrite-aes-settings-4' of github.com:zip-rs/zip2 into …
Its-Just-Nans f4a5778
Merge branch 'rewrite-aes-settings-4' into rewrite-extra-fields
Its-Just-Nans 11686f8
fmt
Its-Just-Nans 6189e6b
refactor: update some extra field
Its-Just-Nans 98f6055
Update src/write.rs
Its-Just-Nans ee55cd3
Update src/write.rs
Its-Just-Nans 871baa3
Update src/write.rs
Pr0methean 6264c79
cleanup gemini
Its-Just-Nans a77dc58
remove magic
Its-Just-Nans 9e547ce
Merge branch 'master' into update-extra-field
Its-Just-Nans e2fd83f
add test update_aes
Its-Just-Nans bb14ec5
refactor: move to format directory
Its-Just-Nans 9aa4770
fix some docs
Its-Just-Nans 778506c
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans b95254c
Merge branch 'refactor-zip-format' into rewrite-extra-fields
Its-Just-Nans dc75dc2
fmt
Its-Just-Nans 3391ebe
cargo fmrt
Its-Just-Nans e534c95
Merge branch 'refactor-zip-format' into rewrite-extra-fields
Its-Just-Nans 9fee34c
sync
Its-Just-Nans 0916719
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans 112cb87
update imports
Its-Just-Nans 86e3eb7
rewrite a little
Its-Just-Nans 50c794a
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans 7dac85a
more change
Its-Just-Nans 2a24162
fmt
Its-Just-Nans ad30a8f
add more code
Its-Just-Nans fb72f74
update
Its-Just-Nans d247b48
fix
Its-Just-Nans 4e53592
refactor: change extra_data to extra_fields
Its-Just-Nans c557aa3
add arbitrary
Its-Just-Nans d2a6903
fix
Its-Just-Nans dff9f14
chang
Its-Just-Nans d137d10
remove ignore
Its-Just-Nans 6a94280
refactor: better code for next step
Its-Just-Nans 16e4fbd
clippy
Its-Just-Nans 82a4cb1
refactor: change zip64 parsing
Its-Just-Nans 5446827
Merge branch 'refactor-zip64' into rewrite-extra-fields
Its-Just-Nans 4798441
Merge branch 'refactor-extra-data' into rewrite-extra-fields
Its-Just-Nans f2acd59
cleanup
Its-Just-Nans 6de54db
more progress & cargo fmt
Its-Just-Nans 33a15ba
updat
Its-Just-Nans 7a1b44b
add deprecated
Its-Just-Nans 2f61a2c
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans 89f3a48
update
Its-Just-Nans db8637a
update
Its-Just-Nans 901eff6
add
Its-Just-Nans 2f81cff
now I need to comply with tests
Its-Just-Nans 4e0700e
more
Its-Just-Nans 4ca9ca9
cleanup
Its-Just-Nans b49d565
apply the extra fields
Its-Just-Nans 93312d6
update
Its-Just-Nans 95f9f7a
add zip
Its-Just-Nans 2802019
handle zip64
Its-Just-Nans da61384
write zip64
Its-Just-Nans 9c5d75b
fix some tests
Its-Just-Nans da49f39
debug and fix some tests
Its-Just-Nans 3a8a582
remove old NoOp
Its-Just-Nans c22f612
fix zip64 and cleanup
Its-Just-Nans f21e1be
Update src/extra_fields/mod.rs
Its-Just-Nans 8e8bc6e
Update src/extra_fields/mod.rs
Its-Just-Nans 7e82dbe
fix: update
Its-Just-Nans d68d57f
Merge branch 'master' into refactor-extra-data
Its-Just-Nans edd138d
Merge branch 'master' into refactor-extra-data
Its-Just-Nans 7cf479a
Merge branch 'refactor-extra-data' into rewrite-extra-fields
Its-Just-Nans ef4bcc3
Merge branch 'master' into refactor-extra-data
Its-Just-Nans 528357e
fix: move to CustomExtraField
Its-Just-Nans 5b05880
merge update
Its-Just-Nans b1b6bff
add comment
Its-Just-Nans 58eefb7
Merge branch 'refactor-extra-data' into rewrite-extra-fields
Its-Just-Nans 5be419f
merge again
Its-Just-Nans 032a324
add test
Its-Just-Nans e136db3
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans a310092
little cleanup
Its-Just-Nans e89633d
return None when empty
Its-Just-Nans 9ac374d
fix import
Its-Just-Nans f2536cf
better fix because Vec is no alloc by default
Its-Just-Nans 693636c
use is_err()
Its-Just-Nans ea6f407
add cfg attr
Its-Just-Nans 9d360c3
add not
Its-Just-Nans 6b70893
add comment
Its-Just-Nans 85fe3d5
fix: some gemini
Its-Just-Nans 7dbe6cd
handle too long extra field when starting entry
Its-Just-Nans fabe9ff
fix: typos
Its-Just-Nans 0dabe17
Merge branch 'master' into rewrite-extra-fields
Its-Just-Nans 2eac315
fix: add the filtering of the extra fields
Its-Just-Nans b3c8ea5
rename
Its-Just-Nans e1aa874
use ExtraFields::parse
Its-Just-Nans e169694
chore: typo
Its-Just-Nans File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,3 +4,4 @@ target | |
| \.idea/ | ||
| /fuzz_read/out/ | ||
| /fuzz_write/out/ | ||
| zipdetails | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,315 @@ | ||
| //! Code related to the `ExtraField` enum | ||
|
|
||
| use crate::extra_fields::AexEncryption; | ||
| use crate::extra_fields::CustomExtraField; | ||
| use crate::extra_fields::ExtendedTimestamp; | ||
| use crate::extra_fields::Ntfs; | ||
| use crate::extra_fields::UnicodeExtraField; | ||
| use crate::extra_fields::UsedExtraField; | ||
| use crate::extra_fields::Zip64ExtendedInformation; | ||
| use crate::format::flags::ZipFlags; | ||
| use crate::result::ZipResult; | ||
| use crate::result::invalid; | ||
| use crate::spec::BlockGetter; | ||
| use crate::types::ZipFileData; | ||
| use crate::unstable::LittleEndianReadExt; | ||
| use core::mem; | ||
| use std::io::ErrorKind; | ||
| use std::io::{Cursor, Read, Write}; | ||
|
|
||
| /// contains one extra field | ||
| #[derive(Debug, Clone)] | ||
| #[non_exhaustive] | ||
| pub enum ExtraField { | ||
| /// NTFS extra field | ||
| Ntfs(Ntfs), | ||
|
|
||
| /// extended timestamp, as described in <https://libzip.org/specifications/extrafld.txt> | ||
| ExtendedTimestamp(ExtendedTimestamp), | ||
|
|
||
| /// AeX Encryption | ||
| AeXEncryption(AexEncryption), | ||
| /// Zip64 Information | ||
| Zip64ExtendedInformation { | ||
| /// uncompressed size | ||
| uncompressed_size: Option<u64>, | ||
| /// compressed size | ||
| compressed_size: Option<u64>, | ||
| /// header start | ||
| header_start: Option<u64>, | ||
| }, | ||
| /// Unicode Comment | ||
| UnicodeComment(UnicodeExtraField), | ||
| /// UnicodePath | ||
| UnicodePath(UnicodeExtraField), | ||
| /// Data Stream Alignment | ||
| DataStreamAlignment(u64), | ||
| /// Custom extra field | ||
| Custom(CustomExtraField), | ||
| } | ||
|
|
||
| /// Extra fields list | ||
| #[derive(Debug, Clone, Default)] | ||
| pub struct ExtraFields { | ||
| pub(crate) inner: Vec<ExtraField>, | ||
| } | ||
|
|
||
| impl ExtraFields { | ||
| pub(crate) fn parse<B: BlockGetter>(buff: &[u8], block: &B) -> ZipResult<Self> { | ||
| let mut reader = Cursor::new(buff); | ||
| let mut extra_fields = Vec::new(); | ||
| while (reader.position() as usize) < buff.len() { | ||
| let parsed_extra_field = ExtraField::parse(&mut reader, block)?; | ||
| let Some(parsed_extra_field) = parsed_extra_field else { | ||
| break; | ||
| }; | ||
| extra_fields.push(parsed_extra_field); | ||
| } | ||
| Ok(Self { | ||
| inner: extra_fields, | ||
| }) | ||
| } | ||
|
|
||
| pub(crate) fn local_extra_fields_mut(&mut self) -> std::slice::IterMut<'_, ExtraField> { | ||
| self.inner.iter_mut() | ||
| } | ||
|
|
||
| pub(crate) fn local_extra_fields(&self) -> std::slice::Iter<'_, ExtraField> { | ||
| self.inner.iter() | ||
| } | ||
|
|
||
| pub(crate) fn central_extra_fields(&self) -> std::slice::Iter<'_, ExtraField> { | ||
| self.inner.iter() | ||
| } | ||
|
Its-Just-Nans marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| impl ExtraField { | ||
| pub(crate) fn parse<R: Read, B: BlockGetter>( | ||
| reader: &mut R, | ||
| file: &B, | ||
| ) -> ZipResult<Option<Self>> { | ||
| let extra_field_header_id = match reader.read_u16_le() { | ||
| Ok(value) => value, | ||
| Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(None), | ||
| Err(e) => return Err(e.into()), | ||
| }; | ||
| let decoded_extra_field = UsedExtraField::try_from(extra_field_header_id); | ||
| let len = match decoded_extra_field { | ||
| Ok(known_field) => match reader.read_u16_le() { | ||
| Ok(len) => len, | ||
| Err(e) if e.kind() == ErrorKind::UnexpectedEof => { | ||
| return Err(invalid!("Extra field {} header truncated", known_field)); | ||
| } | ||
| Err(e) => return Err(e.into()), | ||
| }, | ||
| Err(()) => { | ||
| match reader.read_u16_le() { | ||
| Ok(len) => len, | ||
| Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(None), // early return, most likely a padding | ||
| Err(_e) => { | ||
| // Consume remaining bytes to avoid infinite loop in caller | ||
| let mut buf = Vec::new(); | ||
| let _ = reader.read_to_end(&mut buf); | ||
|
Its-Just-Nans marked this conversation as resolved.
Outdated
|
||
| return Ok(None); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| let parsed_extra_field = match decoded_extra_field { | ||
| // Zip64 extended information extra field | ||
| Ok(UsedExtraField::Zip64ExtendedInfo) => { | ||
| let (new_uncomp, new_comp, new_head) = Zip64ExtendedInformation::parse( | ||
| reader, | ||
| len, | ||
| file.get_uncompressed_size(), | ||
| file.get_compressed_size(), | ||
| file.get_header_start(), | ||
| )?; | ||
| ExtraField::Zip64ExtendedInformation { | ||
| uncompressed_size: Some(new_uncomp), | ||
| compressed_size: Some(new_comp), | ||
| header_start: Some(new_head), | ||
| } | ||
| } | ||
| Ok(UsedExtraField::Ntfs) => { | ||
| // NTFS extra field | ||
| ExtraField::Ntfs(Ntfs::try_from_reader(reader, len)?) | ||
| } | ||
| Ok(UsedExtraField::AeXEncryption) => { | ||
| // AES | ||
| let (new_aes_enc, inner_compression) = AexEncryption::parse(reader, len)?; | ||
| ExtraField::AeXEncryption(AexEncryption::new( | ||
| new_aes_enc.1, | ||
| new_aes_enc.0, | ||
| inner_compression, | ||
| )) | ||
| } | ||
| Ok(UsedExtraField::ExtendedTimestamp) => { | ||
| ExtraField::ExtendedTimestamp(ExtendedTimestamp::try_from_reader(reader, len)?) | ||
| } | ||
| Ok(UsedExtraField::UnicodeComment) => { | ||
| // Info-ZIP Unicode Comment Extra Field | ||
| // APPNOTE 4.6.8 and https://libzip.org/specifications/extrafld.txt | ||
| let unicode = UnicodeExtraField::try_from_reader(reader, len)?; | ||
| ExtraField::UnicodeComment(unicode) | ||
| } | ||
| Ok(UsedExtraField::UnicodePath) => { | ||
| // Info-ZIP Unicode Path Extra Field | ||
| // APPNOTE 4.6.9 and https://libzip.org/specifications/extrafld.txt | ||
| let unicode = UnicodeExtraField::try_from_reader(reader, len)?; | ||
| ExtraField::UnicodePath(unicode) | ||
| } | ||
| _ => { | ||
| let mut buf = vec![0u8; len as usize]; | ||
| if let Err(e) = reader.read_exact(&mut buf) { | ||
| if e.kind() == ErrorKind::UnexpectedEof { | ||
| return Err(invalid!("Extra field content truncated")); | ||
| } | ||
| return Err(e.into()); | ||
| } | ||
| ExtraField::Custom(CustomExtraField::new(false, extra_field_header_id, &buf)) | ||
| // Other fields are ignored | ||
| } | ||
| }; | ||
| Ok(Some(parsed_extra_field)) | ||
| } | ||
|
|
||
| pub(crate) fn size(&self, is_local_header: bool) -> usize { | ||
| match self { | ||
| // Zip64 extended information extra field | ||
| ExtraField::Zip64ExtendedInformation { | ||
| uncompressed_size, | ||
| compressed_size, | ||
| header_start, | ||
| } => { | ||
| let mut size = mem::size_of::<UsedExtraField>() + mem::size_of::<u16>(); | ||
| if uncompressed_size.is_some() { | ||
| size += mem::size_of::<u64>(); | ||
| } | ||
| if compressed_size.is_some() { | ||
| size += mem::size_of::<u64>(); | ||
| } | ||
| if !is_local_header && header_start.is_some() { | ||
| size += mem::size_of::<u64>(); | ||
| } | ||
| size | ||
| } | ||
| ExtraField::Ntfs(_ntfs) => { | ||
| // NTFS extra field | ||
| 0 | ||
| } | ||
| ExtraField::AeXEncryption { .. } => AexEncryption::FULL_SIZE, | ||
| ExtraField::ExtendedTimestamp(_extended_timestamp) => { | ||
| // nothing to do | ||
| 0 | ||
| } | ||
| ExtraField::UnicodeComment(unicode_comment) => unicode_comment.full_size(), | ||
| ExtraField::UnicodePath(unicode_path) => unicode_path.full_size(), | ||
| ExtraField::Custom(custom) => custom.len_with_header(), | ||
| _ => 0, | ||
| } | ||
| } | ||
|
|
||
| pub(crate) fn write<W: Write>(&self, writer: &mut W, is_local_header: bool) -> ZipResult<()> { | ||
| match self { | ||
| // Zip64 extended information extra field | ||
| ExtraField::Zip64ExtendedInformation { | ||
| compressed_size, | ||
| uncompressed_size, | ||
| header_start, | ||
| } => { | ||
| let magic = UsedExtraField::Zip64ExtendedInfo.as_u16(); | ||
| writer.write_all(&magic.to_le_bytes())?; | ||
| let size = self.size(is_local_header); | ||
| let size = size - mem::size_of::<u16>() - mem::size_of::<u16>(); | ||
| let size = size as u16; | ||
| writer.write_all(&size.to_le_bytes())?; | ||
| if let Some(uncomp_size) = uncompressed_size { | ||
| writer.write_all(&uncomp_size.to_le_bytes())?; | ||
| } | ||
| if let Some(comp_size) = compressed_size { | ||
| writer.write_all(&comp_size.to_le_bytes())?; | ||
| } | ||
| if !is_local_header && let Some(head_start) = header_start { | ||
| writer.write_all(&head_start.to_le_bytes())?; | ||
| } | ||
|
Its-Just-Nans marked this conversation as resolved.
|
||
| } | ||
| ExtraField::AeXEncryption(aex) => { | ||
| aex.write(writer)?; | ||
| } | ||
| ExtraField::Custom(custom) => { | ||
| custom.write(writer)?; | ||
| } | ||
| ExtraField::UnicodeComment(unicode_comment) => { | ||
| let magic = UsedExtraField::UnicodeComment.as_u16(); | ||
| writer.write_all(&magic.to_le_bytes())?; | ||
| unicode_comment.write(writer)?; | ||
| } | ||
| ExtraField::UnicodePath(unicode_path) => { | ||
| let magic = UsedExtraField::UnicodePath.as_u16(); | ||
| writer.write_all(&magic.to_le_bytes())?; | ||
| unicode_path.write(writer)?; | ||
| } | ||
| _ => { | ||
| // nothing to do | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| impl ZipFileData { | ||
| pub(crate) fn apply_extra_fields(&mut self, file_name_raw: &mut Vec<u8>) -> ZipResult<()> { | ||
| for one_extra_field in &self.extra_fields.inner { | ||
| match one_extra_field { | ||
| // Zip64 extended information extra field | ||
| ExtraField::Zip64ExtendedInformation { | ||
| uncompressed_size, | ||
| compressed_size, | ||
| header_start, | ||
| } => { | ||
| self.large_file = true; | ||
| if let Some(uncomp_size) = *uncompressed_size { | ||
| self.uncompressed_size = uncomp_size; | ||
| } | ||
| if let Some(comp_size) = *compressed_size { | ||
| self.compressed_size = comp_size; | ||
| } | ||
| if let Some(head_start) = *header_start { | ||
| self.header_start = head_start; | ||
| } | ||
| } | ||
| ExtraField::AeXEncryption(AexEncryption { | ||
| aes_mode, | ||
| aes_vendor_version, | ||
| compression_method, | ||
| .. | ||
| }) => { | ||
| self.aes_mode = Some((*aes_mode, *aes_vendor_version)); | ||
| self.compression_method = *compression_method; | ||
| } | ||
| ExtraField::UnicodeComment(unicode) | ||
| // Info-ZIP Unicode Comment Extra Field | ||
| // APPNOTE 4.6.8 and https://libzip.org/specifications/extrafld.txt | ||
| // If the CRC check fails, this Unicode Comment extra field SHOULD be ignored and | ||
| // the File Comment field in the header SHOULD be used instead. | ||
| if unicode.is_crc32_valid(self.file_comment.as_bytes()) => { | ||
| self.file_comment = String::from_utf8(unicode.content.to_vec())?.into(); | ||
| } | ||
|
Its-Just-Nans marked this conversation as resolved.
Outdated
|
||
| ExtraField::UnicodePath(unicode) | ||
| // Info-ZIP Unicode Path Extra Field | ||
| // APPNOTE 4.6.9 and https://libzip.org/specifications/extrafld.txt | ||
| // If the CRC check fails, this UTF-8 Path Extra Field SHOULD be ignored and | ||
| // the File Name field in the header SHOULD be used instead. | ||
| if unicode.is_crc32_valid(file_name_raw) => { | ||
| *file_name_raw = unicode.content.to_vec(); | ||
| self.flags |= ZipFlags::LanguageEncoding.as_u16(); | ||
| } | ||
|
Its-Just-Nans marked this conversation as resolved.
Outdated
|
||
| _ => { | ||
| // nothing to do | ||
| } | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.