diff options
Diffstat (limited to 'Lib/tarfile.py')
| -rwxr-xr-x | Lib/tarfile.py | 95 | 
1 files changed, 59 insertions, 36 deletions
| diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 5f1a979ad0..ca45126ff1 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1414,9 +1414,9 @@ class TarFile(object):             can be determined, `mode' is overridden by `fileobj's mode.             `fileobj' is not closed, when TarFile is closed.          """ -        modes = {"r": "rb", "a": "r+b", "w": "wb"} +        modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"}          if mode not in modes: -            raise ValueError("mode must be 'r', 'a' or 'w'") +            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")          self.mode = mode          self._mode = modes[mode] @@ -1488,7 +1488,7 @@ class TarFile(object):                      except HeaderError as e:                          raise ReadError(str(e)) -            if self.mode in "aw": +            if self.mode in ("a", "w", "x"):                  self._loaded = True                  if self.pax_headers: @@ -1529,6 +1529,15 @@ class TarFile(object):             'w:bz2'      open for writing with bzip2 compression             'w:xz'       open for writing with lzma compression +           'x' or 'x:'  create a tarfile exclusively without compression, raise +                        an exception if the file is already created +           'x:gz'       create an gzip compressed tarfile, raise an exception +                        if the file is already created +           'x:bz2'      create an bzip2 compressed tarfile, raise an exception +                        if the file is already created +           'x:xz'       create an lzma compressed tarfile, raise an exception +                        if the file is already created +             'r|*'        open a stream of tar blocks with transparent compression             'r|'         open an uncompressed stream of tar blocks for reading             'r|gz'       open a gzip compressed stream of tar blocks @@ -1587,7 +1596,7 @@ class TarFile(object):              t._extfileobj = False              return t -        elif mode in ("a", "w"): +        elif mode in ("a", "w", "x"):              return cls.taropen(name, mode, fileobj, **kwargs)          raise ValueError("undiscernible mode") @@ -1596,8 +1605,8 @@ class TarFile(object):      def taropen(cls, name, mode="r", fileobj=None, **kwargs):          """Open uncompressed tar archive name for reading or writing.          """ -        if mode not in ("r", "a", "w"): -            raise ValueError("mode must be 'r', 'a' or 'w'") +        if mode not in ("r", "a", "w", "x"): +            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")          return cls(name, mode, fileobj, **kwargs)      @classmethod @@ -1605,8 +1614,8 @@ class TarFile(object):          """Open gzip compressed tar archive name for reading or writing.             Appending is not allowed.          """ -        if mode not in ("r", "w"): -            raise ValueError("mode must be 'r' or 'w'") +        if mode not in ("r", "w", "x"): +            raise ValueError("mode must be 'r', 'w' or 'x'")          try:              import gzip @@ -1639,8 +1648,8 @@ class TarFile(object):          """Open bzip2 compressed tar archive name for reading or writing.             Appending is not allowed.          """ -        if mode not in ("r", "w"): -            raise ValueError("mode must be 'r' or 'w'.") +        if mode not in ("r", "w", "x"): +            raise ValueError("mode must be 'r', 'w' or 'x'")          try:              import bz2 @@ -1668,8 +1677,8 @@ class TarFile(object):          """Open lzma compressed tar archive name for reading or writing.             Appending is not allowed.          """ -        if mode not in ("r", "w"): -            raise ValueError("mode must be 'r' or 'w'") +        if mode not in ("r", "w", "x"): +            raise ValueError("mode must be 'r', 'w' or 'x'")          try:              import lzma @@ -1711,7 +1720,7 @@ class TarFile(object):          self.closed = True          try: -            if self.mode in "aw": +            if self.mode in ("a", "w", "x"):                  self.fileobj.write(NUL * (BLOCKSIZE * 2))                  self.offset += (BLOCKSIZE * 2)                  # fill up the end with zero-blocks @@ -1757,7 +1766,7 @@ class TarFile(object):             addfile(). If given, `arcname' specifies an alternative name for the             file in the archive.          """ -        self._check("aw") +        self._check("awx")          # When fileobj is given, replace name by          # fileobj's real name. @@ -1848,14 +1857,17 @@ class TarFile(object):                  tarinfo.devminor = os.minor(statres.st_rdev)          return tarinfo -    def list(self, verbose=True): +    def list(self, verbose=True, *, members=None):          """Print a table of contents to sys.stdout. If `verbose' is False, only             the names of the members are printed. If it is True, an `ls -l'-like -           output is produced. +           output is produced. `members' is optional and must be a subset of the +           list returned by getmembers().          """          self._check() -        for tarinfo in self: +        if members is None: +            members = self +        for tarinfo in members:              if verbose:                  _safe_print(stat.filemode(tarinfo.mode))                  _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, @@ -1888,7 +1900,7 @@ class TarFile(object):             TarInfo object, if it returns None the TarInfo object will be             excluded from the archive.          """ -        self._check("aw") +        self._check("awx")          if arcname is None:              arcname = name @@ -1945,7 +1957,7 @@ class TarFile(object):             On Windows platforms, `fileobj' should always be opened with mode             'rb' to avoid irritation about the file size.          """ -        self._check("aw") +        self._check("awx")          tarinfo = copy.copy(tarinfo) @@ -1964,12 +1976,13 @@ class TarFile(object):          self.members.append(tarinfo) -    def extractall(self, path=".", members=None): +    def extractall(self, path=".", members=None, *, numeric_owner=False):          """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(). +           list returned by getmembers(). If `numeric_owner` is True, only +           the numbers for user/group names are used and not the names.          """          directories = [] @@ -1983,7 +1996,8 @@ class TarFile(object):                  tarinfo = copy.copy(tarinfo)                  tarinfo.mode = 0o700              # Do not set_attrs directories, as we will do that further down -            self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) +            self.extract(tarinfo, path, set_attrs=not tarinfo.isdir(), +                         numeric_owner=numeric_owner)          # Reverse sort directories.          directories.sort(key=lambda a: a.name) @@ -1993,7 +2007,7 @@ class TarFile(object):          for tarinfo in directories:              dirpath = os.path.join(path, tarinfo.name)              try: -                self.chown(tarinfo, dirpath) +                self.chown(tarinfo, dirpath, numeric_owner=numeric_owner)                  self.utime(tarinfo, dirpath)                  self.chmod(tarinfo, dirpath)              except ExtractError as e: @@ -2002,12 +2016,14 @@ class TarFile(object):                  else:                      self._dbg(1, "tarfile: %s" % e) -    def extract(self, member, path="", set_attrs=True): +    def extract(self, member, path="", set_attrs=True, *, numeric_owner=False):          """Extract a member from the archive to the current working directory,             using its full name. Its file information is extracted as accurately             as possible. `member' may be a filename or a TarInfo object. You can             specify a different directory using `path'. File attributes (owner, -           mtime, mode) are set unless `set_attrs' is False. +           mtime, mode) are set unless `set_attrs' is False. If `numeric_owner` +           is True, only the numbers for user/group names are used and not +           the names.          """          self._check("r") @@ -2022,7 +2038,8 @@ class TarFile(object):          try:              self._extract_member(tarinfo, os.path.join(path, tarinfo.name), -                                 set_attrs=set_attrs) +                                 set_attrs=set_attrs, +                                 numeric_owner=numeric_owner)          except OSError as e:              if self.errorlevel > 0:                  raise @@ -2068,7 +2085,8 @@ class TarFile(object):              # blkdev, etc.), return None instead of a file object.              return None -    def _extract_member(self, tarinfo, targetpath, set_attrs=True): +    def _extract_member(self, tarinfo, targetpath, set_attrs=True, +                        numeric_owner=False):          """Extract the TarInfo object tarinfo to a physical             file called targetpath.          """ @@ -2106,7 +2124,7 @@ class TarFile(object):              self.makefile(tarinfo, targetpath)          if set_attrs: -            self.chown(tarinfo, targetpath) +            self.chown(tarinfo, targetpath, numeric_owner)              if not tarinfo.issym():                  self.chmod(tarinfo, targetpath)                  self.utime(tarinfo, targetpath) @@ -2195,19 +2213,24 @@ class TarFile(object):              except KeyError:                  raise ExtractError("unable to resolve link inside archive") -    def chown(self, tarinfo, targetpath): -        """Set owner of targetpath according to tarinfo. +    def chown(self, tarinfo, targetpath, numeric_owner): +        """Set owner of targetpath according to tarinfo. If numeric_owner +           is True, use .gid/.uid instead of .gname/.uname.          """          if pwd and hasattr(os, "geteuid") and os.geteuid() == 0:              # We have to be root to do so. -            try: -                g = grp.getgrnam(tarinfo.gname)[2] -            except KeyError: +            if numeric_owner:                  g = tarinfo.gid -            try: -                u = pwd.getpwnam(tarinfo.uname)[2] -            except KeyError:                  u = tarinfo.uid +            else: +                try: +                    g = grp.getgrnam(tarinfo.gname)[2] +                except KeyError: +                    g = tarinfo.gid +                try: +                    u = pwd.getpwnam(tarinfo.uname)[2] +                except KeyError: +                    u = tarinfo.uid              try:                  if tarinfo.issym() and hasattr(os, "lchown"):                      os.lchown(targetpath, u, g) | 
