views:

904

answers:

3

I have a file test.txt that is inside a zip archive test.zip. The permissions on test.txt are out of my control when it's compressed, but now I want them to be group-writeable. I am extracting the file with Python, and don't want to escape out to the shell.

EDIT: Here's what I've got so far:

import zipfile

z = zipfile.ZipFile('test.zip', 'w')
zi = zipfile.ZipInfo('test.txt')
zi.external_attr = 0777 << 16L
z.writestr(zi, 'FOO')
z.close()

z = zipfile.ZipFile('test.zip', 'r')
for name in z.namelist():
    newFile = open(name, "wb")
    newFile.write(z.read(name))

    newFile.close()
z.close()

This works perfectly on OS X using 2.5.1, but it doesn't work on my home box (Debian, Python 2.4 & 2.5) or on RHEL 5 with Python 2.4. On anything but OS X it doesn't error, but doesn't change the permissions either. Any ideas why? Also, how does writestr() work? I know I'm using it incorrectly here.

Is there a way to do this without os.chmod (the user extracting the file doesn't have permissions to use os.chmod after it's extracted)? I have full write access to the zip file.

More info:

> ls -l test.zip
-rwxrwxrwx 1 myuser mygroup 2008-11-11 13:24 test.zip
> unzip test.zip
Archive:  test.zip
  inflating: test.txt 
> ls -l test.txt
-rw-r--r-- 1 myuser mygroup 2008-11-11 13:34 test.txt

The user extracting is not myuser, but is in mygroup.

+1  A: 

Per the documentation, unzip sets the permissions to those stored, under unix. Also, the shell umask is not used. Your best bet is to make sure the perms are set before you zip the file.

Since you can't do that, you will have to try and do what you were trying to do (and get it to work under Debian.)

There have been a number of issues with Pythons zipfile library, including setting the mode of writestr to that of the file being written on some systems, or setting the zip systm to windows instead of unix. So your inconsistent results may mean that nothing has changed.

So you may be completely out of luck.

Chris
A: 

Extract to stdout (unzip -p) and redirect to a file? If there's more than one file in the zip, you could list the zip contents, and then extract one at a time.

for n in `unzip -l test.zip | awk 'NR > 3 && NF == 4 { print $4 }'`; do unzip -p test.zip $n > $n; done

(yeah, I know you tagged this 'python' :-) )

John Fouhy
A: 

I had a similar problem to you, so here is the code spinet from my stuff, this I believe should help here.

# extract all of the zip
for file in zf.filelist:
    name = file.filename
    perm = ((file.external_attr >> 16L) & 0777)
    if name.endswith('/'):
        outfile = os.path.join(dir, name)
        os.mkdir(outfile, perm)
    else:
        outfile = os.path.join(dir, name)
        fh = os.open(outfile, os.O_CREAT | os.O_WRONLY , perm)
        os.write(fh, zf.read(name))
        os.close(fh)
    print "Extracting: " + outfile

You might do something similar, but insert your own logic to calculate your perm value. I should note that I'm using Python 2.5 here, I'm aware of a few incompatibilities with some versions of Python's zipfile support.

Petriborg