相关的 Python 问题提供了一些关于为什么首先存在问题的见解:https://bugs.python.org/issue18262 和 https://bugs.python.org/issue15795
此外,可以在此处找到原始 Zip 规范:https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
重要的部分是:
4.4.2版本(2字节)
4.4.2.1 高字节表示文件的兼容性
属性信息。如果外部文件属性
与 MS-DOS 兼容,可以通过 PKZIP 读取
DOS 版本 2.04g 那么这个值将为零。如果这些
属性不兼容,则此值将
识别属性所在的主机系统
兼容的。软件可以使用此信息来确定
文本文件等的行记录格式。
4.4.2.2 目前的映射是:
0 - MS-DOS 和 OS/2(FAT / VFAT / FAT32 文件系统)
1 - 阿米加 2 - OpenVMS
3 - UNIX 4 - 虚拟机/CMS
5 - 雅达利 ST 6 - OS/2 H.P.F.S.
7 - Macintosh 8 - Z 系统
9 - CP/M 10 - Windows NTFS
11 - MVS (OS/390 - Z/OS) 12 - VSE
13 - Acorn Risc 14 - VFAT
15 - 备用 MVS 16 - BeOS
17 - 串联 18 - OS/400
19 - OS X (Darwin) 20 到 255 - 未使用
...
4.4.15 外部文件属性:(4字节)
外部属性的映射是
取决于主机系统(请参阅“制作的版本”)。为了
MS-DOS,低位字节是MS-DOS目录
属性字节。如果输入来自标准输入,则此
字段设置为零。
这意味着外部文件属性是系统特定的。解释不同系统的外部文件属性可能会使情况变得更糟。如果我们只关心 UNIX,我们可以检查 ZipInfo.created_system 并将其与 3(对于 UNIX)进行比较。不幸的是,规范并没有进一步帮助我们解释外部属性。
在这个 Wiki 中有一些东西http://forensicswiki.org/wiki/Zip#External_file_attributes
外部属性 UNIX (3) 大小为 4 个字节,包括:
╔═════════╦══════════╦════════╦═══════════════════ ════════════════════════════════════╗
║ 偏移量 ║ 尺寸 ║ 值 ║ 描述 ║
╠═════════╬══════════╬════════╬═══════════════════ ═════════════════════════════════════
║ 0 ║ 1 ║ ║ FAT (MS-DOS) 文件属性。 ║
║ 1 ║ 1 ║ ║ 未知 ║
║ 2 ║ 16 位 ║ ║ UNIX 模式(或权限)。 ║
║ ║ ║ ║ 该值似乎与 stat.st_mode 值相似。 ║
╚═════════╩══════════╩════════╩═══════════════════ ════════════════════════════════════╝
虽然这只是观察性的,但似乎是共识。
把这些放在一起:
from zipfile import ZipFile
ZIP_UNIX_SYSTEM = 3
def extract_all_with_permission(zf, target_dir):
for info in zf.infolist():
extracted_path = zf.extract(info, target_dir)
if info.create_system == ZIP_UNIX_SYSTEM:
unix_attributes = info.external_attr >> 16
if unix_attributes:
os.chmod(extracted_path, unix_attributes)
with ZipFile('sample.zip', 'r') as zf:
extract_all_with_permission(zf, '/tmp')
可能会出现一个问题,为什么我们首先要保留权限。有些人可能敢说我们只想保留可执行标志。在这种情况下,一个稍微安全一点的选择可能是只为文件恢复可执行标志。
from zipfile import ZipFile
from stat import S_IXUSR
ZIP_UNIX_SYSTEM = 3
def extract_all_with_executable_permission(zf, target_dir):
for info in zf.infolist():
extracted_path = zf.extract(info, target_dir)
if info.create_system == ZIP_UNIX_SYSTEM and os.path.isfile(extracted_path):
unix_attributes = info.external_attr >> 16
if unix_attributes & S_IXUSR:
os.chmod(extracted_path, os.stat(extracted_path).st_mode | S_IXUSR)
with ZipFile('sample.zip', 'r') as zf:
extract_all_with_executable_permission(zf, '/tmp')