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) |