Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ pub(crate) use crate::format::aes::{AesMode, AesVendorVersion};
pub(crate) use crate::format::flags::System;

pub(crate) mod ffi {
pub const S_IFDIR: u32 = 0o0_040_000;
pub const S_IFREG: u32 = 0o0_100_000;
pub const S_IFLNK: u32 = 0o0_120_000;
/// Mask
pub const S_IFMT: u32 = 0b1111_0000_0000_0000;

/// Regular
pub const S_IFREG: u32 = 0b1000_0000_0000_0000; // 0o0_100_000
/// Directory
pub const S_IFDIR: u32 = 0b0100_0000_0000_0000; // 0o0_040_000
/// Symbolic link
pub const S_IFLNK: u32 = 0b1010_0000_0000_0000; // 0o0_120_000
}

pub(crate) struct ZipRawValues {
Expand Down Expand Up @@ -266,28 +272,35 @@ impl ZipFileData {
return None;
}
let unix_mode = self.external_attributes >> 16;
if unix_mode != 0 {
// If the high 16 bits are non-zero, they probably contain Unix permissions.
// This happens for archives created on Windows by this crate or other tools,
// and is the only way to identify symlinks in such archives.
return Some(unix_mode);
}
match self.system {
System::Unix => Some(unix_mode),
System::Dos => {
// For MS-DOS, the low order byte is the MS-DOS directory attribute byte.
let directory_attributes = self.external_attributes & 0xFFFF;
// Interpret MS-DOS directory bit
let mut mode = if 0x10 == (self.external_attributes & 0x10) {
let mut mode = if 0x10 == (directory_attributes & 0x10) {
ffi::S_IFDIR | 0o0775
} else if (unix_mode & ffi::S_IFMT) == ffi::S_IFLNK {
ffi::S_IFLNK | 0o0777
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: The symlink check using unix_mode & ffi::S_IFMT in the System::Dos branch is incorrect. For DOS system archives, the external_attributes follow the MS-DOS specification where only the low 16 bits are meaningful (DOS attributes). The high 16 bits should not be interpreted as Unix mode for DOS systems. This check could incorrectly identify files as symlinks when the high 16 bits happen to match the symlink pattern due to uninitialized or arbitrary data. According to the ZIP specification (APPNOTE.TXT section 4.4.15), for DOS compatibility, only the low byte contains the MS-DOS directory attribute byte.

} else {
ffi::S_IFREG | 0o0664
};
if 0x01 == (self.external_attributes & 0x01) {
// Read-only bit; strip write permissions
// Interpret MS-DOS read-only bit
if 0x01 == (directory_attributes & 0x01) {
// strip write permissions for read-only
mode &= !0o222;
}
Some(mode)
}
_ => None,
_ => {
if unix_mode != 0 {
// If the high 16 bits are non-zero, they probably contain Unix permissions.
// This happens for archives created on Windows by this crate or other tools,
// and is the only way to identify symlinks in such archives.
return Some(unix_mode);
}
None
},
}
}

Expand Down
Loading