How do I set permissions (attributes) on a file in a ZIP file using Python's zipfile module?

前端 未结 7 2114
甜味超标
甜味超标 2020-11-30 06:20

When I extract files from a ZIP file created with the Python zipfile module, all the files are not writable, read only etc.

The file is being created and extracted u

相关标签:
7条回答
  • 2020-11-30 06:50

    Also look at what Python's zipfile module does:

    def write(self, filename, arcname=None, compress_type=None):
        ...
        st = os.stat(filename)
        ...
        zinfo = ZipInfo(arcname, date_time)
        zinfo.external_attr = (st[0] & 0xFFFF) << 16L      # Unix attributes
        ...
    

    ```

    0 讨论(0)
  • 2020-11-30 06:53

    Look at this: Set permissions on a compressed file in python

    I'm not entirely sure if that's what you want, but it seems to be.

    The key line appears to be:

    zi.external_attr = 0777 << 16L
    

    It looks like it sets the permissions to 0777 there.

    0 讨论(0)
  • 2020-11-30 06:55

    You can extend the ZipFile class to change the default file permission:

    from zipfile import ZipFile, ZipInfo
    import time
    
    class PermissiveZipFile(ZipFile):
        def writestr(self, zinfo_or_arcname, data, compress_type=None):
            if not isinstance(zinfo_or_arcname, ZipInfo):
                zinfo = ZipInfo(filename=zinfo_or_arcname,
                                date_time=time.localtime(time.time())[:6])
    
                zinfo.compress_type = self.compression
                if zinfo.filename[-1] == '/':
                    zinfo.external_attr = 0o40775 << 16   # drwxrwxr-x
                    zinfo.external_attr |= 0x10           # MS-DOS directory flag
                else:
                    zinfo.external_attr = 0o664 << 16     # ?rw-rw-r--
            else:
                zinfo = zinfo_or_arcname
    
            super(PermissiveZipFile, self).writestr(zinfo, data, compress_type)
    

    This example changes the default file permission to 664 and keeps 775 for directories.

    Related code:

    • ZipFile.writestr, Python 2.7
    • ZipFile.writestr, Python 3.6
    0 讨论(0)
  • 2020-11-30 07:06

    The earlier answers did not work for me (on OS X 10.12). I found that as well as the executable flags (octal 755), I also need to set the "regular file" flag (octal 100000). I found this mentioned here: https://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute

    A complete example:

    zipname = "test.zip"
    filename = "test-executable"
    
    zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
    
    f = open(filename, 'r')
    bytes = f.read()
    f.close()
    
    info = zipfile.ZipInfo(filename)
    info.date_time = time.localtime()
    info.external_attr = 0100755 << 16L
    
    zip.writestr(info, bytes, zipfile.ZIP_DEFLATED)
    
    zip.close()
    

    A complete example of my specific usecase, creating a zip of a .app so that everything in the folder Contents/MacOS/ is executable: https://gist.github.com/Draknek/3ce889860cea4f59838386a79cc11a85

    0 讨论(0)
  • 2020-11-30 07:08

    When you do it like this, does it work alright?

    zf = zipfile.ZipFile("something.zip")
    for name in zf.namelist():
        f = open(name, 'wb')
        f.write(self.read(name))
        f.close()
    

    If not, I'd suggest throwing in an os.chmod in the for loop with 0777 permissions like this:

    zf = zipfile.ZipFile("something.zip")
    for name in zf.namelist():
        f = open(name, 'wb')
        f.write(self.read(name))
        f.close()
        os.chmod(name, 0777)
    
    0 讨论(0)
  • 2020-11-30 07:12

    This link has more information than anything else I've been able to find on the net. Even the zip source doesn't have anything. Copying the relevant section for posterity. This patch isn't really about documenting this format, which just goes to show how pathetic (read non-existent) the current documentation is.

    # external_attr is 4 bytes in size. The high order two
    # bytes represent UNIX permission and file type bits,
    # while the low order two contain MS-DOS FAT file
    # attributes, most notably bit 4 marking directories.
    if node.isfile:
        zipinfo.compress_type = ZIP_DEFLATED
        zipinfo.external_attr = 0644 << 16L # permissions -r-wr--r--
        data = node.get_content().read()
        properties = node.get_properties()
        if 'svn:special' in properties and \
               data.startswith('link '):
            data = data[5:]
            zipinfo.external_attr |= 0120000 << 16L # symlink file type
            zipinfo.compress_type = ZIP_STORED
        if 'svn:executable' in properties:
            zipinfo.external_attr |= 0755 << 16L # -rwxr-xr-x
        zipfile.writestr(zipinfo, data)
    elif node.isdir and path:
        if not zipinfo.filename.endswith('/'):
            zipinfo.filename += '/'
        zipinfo.compress_type = ZIP_STORED
        zipinfo.external_attr = 040755 << 16L # permissions drwxr-xr-x
        zipinfo.external_attr |= 0x10 # MS-DOS directory flag
        zipfile.writestr(zipinfo, '')
    

    Also, this link has the following. Here the low order byte presumably means the rightmost (lowest) byte of the four bytes. So this one is for MS-DOS and can presumably be left as zero otherwise.

    external file attributes: (4 bytes)

          The mapping of the external attributes is
          host-system dependent (see 'version made by').  For
          MS-DOS, the low order byte is the MS-DOS directory
          attribute byte.  If input came from standard input, this
          field is set to zero.
    

    Also, the source file unix/unix.c in the sources for InfoZIP's zip program, downloaded from Debian's archives has the following in comments.

      /* lower-middle external-attribute byte (unused until now):
       *   high bit        => (have GMT mod/acc times) >>> NO LONGER USED! <<<
       *   second-high bit => have Unix UID/GID info
       * NOTE: The high bit was NEVER used in any official Info-ZIP release,
       *       but its future use should be avoided (if possible), since it
       *       was used as "GMT mod/acc times local extra field" flags in Zip beta
       *       versions 2.0j up to 2.0v, for about 1.5 years.
       */
    

    So taking all this together, it looks like only the second highest byte is actually used, at least for Unix.

    EDIT: I asked about the Unix aspect of this on Unix.SX, in the question "The zip format's external file attribute". Looks like I got a couple of things wrong. Specifically both of the top two bytes are used for Unix.

    0 讨论(0)
提交回复
热议问题