52 lines
1.6 KiB
Python
52 lines
1.6 KiB
Python
import os.path
|
|
import logging
|
|
import copy
|
|
import operator
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def safe_extractall(tar, path):
|
|
"""Extract all members from the archive to the current working
|
|
directory and set owner, modification time and permissions on
|
|
directories afterwards. `path' specifies a different directory
|
|
to extract to. `members' is optional and must be a subset of the
|
|
list returned by getmembers().
|
|
"""
|
|
abs_extract_path = os.path.abspath(path)
|
|
def is_member_safe(member):
|
|
""" Returns True if the member is safe, False otherwise. """
|
|
if member.issym():
|
|
link_tar_path = os.path.dirname(member.path)
|
|
abs_link_path = os.path.abspath(os.path.join(path, link_tar_path, member.linkpath))
|
|
if not abs_link_path.startswith(abs_extract_path):
|
|
logger.warning('Filtering symlink outside of extract dir: %s', member.linkpath)
|
|
return False
|
|
elif member.isblk():
|
|
logger.warning('Filtering block device from tarfile: %s', member.path)
|
|
return False
|
|
|
|
return True
|
|
|
|
directories = []
|
|
|
|
for tarinfo in tar:
|
|
if is_member_safe(tarinfo):
|
|
if tarinfo.isdir():
|
|
# Extract directories with a safe mode.
|
|
directories.append(tarinfo)
|
|
tarinfo = copy.copy(tarinfo)
|
|
tarinfo.mode = 0700
|
|
tar.extract(tarinfo, path)
|
|
|
|
# Reverse sort directories.
|
|
directories.sort(key=operator.attrgetter('name'))
|
|
directories.reverse()
|
|
|
|
# Set correct owner, mtime and filemode on directories.
|
|
for tarinfo in directories:
|
|
dirpath = os.path.join(path, tarinfo.name)
|
|
tar.chown(tarinfo, dirpath)
|
|
tar.utime(tarinfo, dirpath)
|
|
tar.chmod(tarinfo, dirpath)
|