diff --git a/AUTHORS b/AUTHORS index 82938eea88..4a7396a7a9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -63,6 +63,7 @@ answer newbie questions, and generally made Django that much better: Anssi Kääriäinen ant9000@netwise.it Anthony Briggs + Anton Samarchyan Antoni Aloy Antonio Cavedoni Antonis Christofides diff --git a/django/utils/archive.py b/django/utils/archive.py index f8c1a0f83b..57e87658a6 100644 --- a/django/utils/archive.py +++ b/django/utils/archive.py @@ -23,6 +23,7 @@ THE SOFTWARE. """ import os import shutil +import stat import tarfile import zipfile @@ -98,6 +99,16 @@ class BaseArchive(object): """ Base Archive class. Implementations should inherit this class. """ + @staticmethod + def _copy_permissions(mode, filename): + """ + If the file in the archive has some permissions (this assumes a file + won't be writable/executable without being readable), apply those + permissions to the unarchived file. + """ + if mode & stat.S_IROTH: + os.chmod(filename, mode) + def split_leading_dir(self, path): path = str(path) path = path.lstrip('/').lstrip('\\') @@ -164,7 +175,7 @@ class TarArchive(BaseArchive): os.makedirs(dirname) with open(filename, 'wb') as outfile: shutil.copyfileobj(extracted, outfile) - os.chmod(filename, member.mode) + self._copy_permissions(member.mode, filename) finally: if extracted: extracted.close() @@ -200,9 +211,9 @@ class ZipArchive(BaseArchive): else: with open(filename, 'wb') as outfile: outfile.write(data) - # convert ZipInfo.external_attr to mode + # Convert ZipInfo.external_attr to mode mode = info.external_attr >> 16 - os.chmod(filename, mode) + self._copy_permissions(mode, filename) def close(self): self._archive.close() diff --git a/tests/utils_tests/archives/foobar.tar b/tests/utils_tests/archives/foobar.tar index 44886fb33b..7e619b41d6 100644 Binary files a/tests/utils_tests/archives/foobar.tar and b/tests/utils_tests/archives/foobar.tar differ diff --git a/tests/utils_tests/archives/foobar.tar.bz2 b/tests/utils_tests/archives/foobar.tar.bz2 index 5a3d8a402e..e4e6c6ab68 100644 Binary files a/tests/utils_tests/archives/foobar.tar.bz2 and b/tests/utils_tests/archives/foobar.tar.bz2 differ diff --git a/tests/utils_tests/archives/foobar.tar.gz b/tests/utils_tests/archives/foobar.tar.gz index 8582a87139..a5f06b1308 100644 Binary files a/tests/utils_tests/archives/foobar.tar.gz and b/tests/utils_tests/archives/foobar.tar.gz differ diff --git a/tests/utils_tests/archives/foobar.zip b/tests/utils_tests/archives/foobar.zip index b02bb3e95a..9956385ee2 100644 Binary files a/tests/utils_tests/archives/foobar.zip and b/tests/utils_tests/archives/foobar.zip differ diff --git a/tests/utils_tests/test_archive.py b/tests/utils_tests/test_archive.py index 42f445ce9c..b207a1290a 100644 --- a/tests/utils_tests/test_archive.py +++ b/tests/utils_tests/test_archive.py @@ -51,6 +51,10 @@ class ArchiveTester(object): filepath = os.path.join(self.tmpdir, 'executable') # The file has executable permission. self.assertTrue(os.stat(filepath).st_mode & stat.S_IXOTH) + filepath = os.path.join(self.tmpdir, 'no_permissions') + # The file is readable even though it doesn't have permission data in + # the archive. + self.assertTrue(os.stat(filepath).st_mode & stat.S_IROTH) def test_extract_function_with_leadpath(self): extract(self.archive_lead_path, self.tmpdir)