Last active
December 28, 2015 20:48
-
-
Save epu/7559669 to your computer and use it in GitHub Desktop.
facepalm. on osx cpython 2.7.6, cannot get a zipfile info entry's extended attr to save as 0120755 for a real file, reverts to 0100755.
This file contains 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
#ZIPFILE_CREATE_SYSTEM_FLAG can be 3 (posix/unix) or 19 (modern pk zip archives) | |
import zipfile | |
import os | |
import stat | |
import time | |
""" The following was use ok to write symlink directory entries into a .zipfile just fine. | |
But things got challenging when retrieving symlinks to files. | |
When iterating over file entries: | |
>>for info in zip_file.filelist: | |
file_ = zip_file.extract(info, target_dir) | |
perms = int(info.external_attr >> 16L) # oct(perms) of a symlink with 0755 == 0120775 | |
# oct(perms) becomes 0100775 | |
""" | |
def __write_symlink(self, zip_file, fullpath, member_name, destpath): | |
""" Write a symlink to a real file or dir into a .zip archive. | |
http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute | |
explains that we probably want to literally use the stat for 'is a symlink'. | |
#define S_IFLNK 0120000 /* symbolic link */ | |
See also the horrors of: | |
http://stackoverflow.com/questions/434641/how-do-i-set-permissions-attributes-on-a-file-in-a-zip-file-using-pythons-zip | |
Parts ripped from the zipfile implementation of write() | |
Also, this is a horrible hack workaround. | |
According to the pkzip spec, the extra field on unix types includes either block device info, | |
or symlink file info. Not sure if it works in practice, or how to write it and tsize. | |
4.5.7 -UNIX Extra Field (0x000d): | |
The following is the layout of the UNIX "extra" block. | |
Note: all fields are stored in Intel low-byte/high-byte | |
order. | |
Value Size Description | |
----- ---- ----------- | |
(UNIX) 0x000d 2 bytes Tag for this "extra" block type | |
TSize 2 bytes Size for the following data block | |
Atime 4 bytes File last access time | |
Mtime 4 bytes File last modification time | |
Uid 2 bytes File user ID | |
Gid 2 bytes File group ID | |
(var) variable Variable length data field | |
The variable length data field will contain file type | |
specific data. Currently the only values allowed are | |
the original "linked to" file names for hard or symbolic | |
links, and the major and minor device node numbers for | |
character and block device nodes. Since device nodes | |
cannot be either symbolic or hard links, only one set of | |
variable length data is stored. Link files will have the | |
name of the original file stored. This name is NOT NULL | |
terminated. Its size can be determined by checking TSize - | |
12. Device entries will have eight bytes stored as two 4 | |
byte entries (in little endian format). The first entry | |
will be the major device number, and the second the minor | |
device number. | |
""" | |
# If you don't explicitly use lstat, | |
# files that are symbolic links will fail stat.S_ISLNK(mode), | |
# and mask will come back as static file (example 0100755). | |
# But having it correct doesn't mean you can write it and get it back. | |
st = os.lstat(fullpath) | |
mtime = time.localtime(st.st_mtime) | |
date_time = mtime[0:6] | |
info = zipfile.ZipInfo(fullpath, date_time) | |
# Slightly better documented with CONSTANT. | |
info.filename = member_name # This might be os.path.basename in the write() impl -epu | |
info.create_system = ZIPFILE_CREATE_SYSTEM_FLAG | |
info.compress_type = zipfile.ZIP_DEFLATED # The example of saving the link from SO uses ZIP_STORED. | |
# But, everytime I create a file that was a symlink, it comes out as regular file mask. | |
# Instead of blindly using only st_mode or setting to constant of 0600, | |
# use the file permissions (S_IMODE of stat result) and the link stat entry. | |
perms = stat.S_IMODE(st.st_mode) | stat.S_IFLNK # oct(perms) when mode is 0775: 0120775 | |
# This might be wrong, since git reports perms of 0120000 internally, no? | |
info.external_attr = perms << 16L | |
#zip_file.writestr(info, member_name) | |
zip_file.writestr(info, destpath) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment