xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/tarfile.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cda5da8dSAndroid Build Coastguard Worker#-------------------------------------------------------------------
3*cda5da8dSAndroid Build Coastguard Worker# tarfile.py
4*cda5da8dSAndroid Build Coastguard Worker#-------------------------------------------------------------------
5*cda5da8dSAndroid Build Coastguard Worker# Copyright (C) 2002 Lars Gustaebel <[email protected]>
6*cda5da8dSAndroid Build Coastguard Worker# All rights reserved.
7*cda5da8dSAndroid Build Coastguard Worker#
8*cda5da8dSAndroid Build Coastguard Worker# Permission  is  hereby granted,  free  of charge,  to  any person
9*cda5da8dSAndroid Build Coastguard Worker# obtaining a  copy of  this software  and associated documentation
10*cda5da8dSAndroid Build Coastguard Worker# files  (the  "Software"),  to   deal  in  the  Software   without
11*cda5da8dSAndroid Build Coastguard Worker# restriction,  including  without limitation  the  rights to  use,
12*cda5da8dSAndroid Build Coastguard Worker# copy, modify, merge, publish, distribute, sublicense, and/or sell
13*cda5da8dSAndroid Build Coastguard Worker# copies  of  the  Software,  and to  permit  persons  to  whom the
14*cda5da8dSAndroid Build Coastguard Worker# Software  is  furnished  to  do  so,  subject  to  the  following
15*cda5da8dSAndroid Build Coastguard Worker# conditions:
16*cda5da8dSAndroid Build Coastguard Worker#
17*cda5da8dSAndroid Build Coastguard Worker# The above copyright  notice and this  permission notice shall  be
18*cda5da8dSAndroid Build Coastguard Worker# included in all copies or substantial portions of the Software.
19*cda5da8dSAndroid Build Coastguard Worker#
20*cda5da8dSAndroid Build Coastguard Worker# THE SOFTWARE IS PROVIDED "AS  IS", WITHOUT WARRANTY OF ANY  KIND,
21*cda5da8dSAndroid Build Coastguard Worker# EXPRESS OR IMPLIED, INCLUDING  BUT NOT LIMITED TO  THE WARRANTIES
22*cda5da8dSAndroid Build Coastguard Worker# OF  MERCHANTABILITY,  FITNESS   FOR  A  PARTICULAR   PURPOSE  AND
23*cda5da8dSAndroid Build Coastguard Worker# NONINFRINGEMENT.  IN  NO  EVENT SHALL  THE  AUTHORS  OR COPYRIGHT
24*cda5da8dSAndroid Build Coastguard Worker# HOLDERS  BE LIABLE  FOR ANY  CLAIM, DAMAGES  OR OTHER  LIABILITY,
25*cda5da8dSAndroid Build Coastguard Worker# WHETHER  IN AN  ACTION OF  CONTRACT, TORT  OR OTHERWISE,  ARISING
26*cda5da8dSAndroid Build Coastguard Worker# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27*cda5da8dSAndroid Build Coastguard Worker# OTHER DEALINGS IN THE SOFTWARE.
28*cda5da8dSAndroid Build Coastguard Worker#
29*cda5da8dSAndroid Build Coastguard Worker"""Read from and write to tar format archives.
30*cda5da8dSAndroid Build Coastguard Worker"""
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard Workerversion     = "0.9.0"
33*cda5da8dSAndroid Build Coastguard Worker__author__  = "Lars Gust\u00e4bel ([email protected])"
34*cda5da8dSAndroid Build Coastguard Worker__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend."
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Worker#---------
37*cda5da8dSAndroid Build Coastguard Worker# Imports
38*cda5da8dSAndroid Build Coastguard Worker#---------
39*cda5da8dSAndroid Build Coastguard Workerfrom builtins import open as bltn_open
40*cda5da8dSAndroid Build Coastguard Workerimport sys
41*cda5da8dSAndroid Build Coastguard Workerimport os
42*cda5da8dSAndroid Build Coastguard Workerimport io
43*cda5da8dSAndroid Build Coastguard Workerimport shutil
44*cda5da8dSAndroid Build Coastguard Workerimport stat
45*cda5da8dSAndroid Build Coastguard Workerimport time
46*cda5da8dSAndroid Build Coastguard Workerimport struct
47*cda5da8dSAndroid Build Coastguard Workerimport copy
48*cda5da8dSAndroid Build Coastguard Workerimport re
49*cda5da8dSAndroid Build Coastguard Workerimport warnings
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Workertry:
52*cda5da8dSAndroid Build Coastguard Worker    import pwd
53*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
54*cda5da8dSAndroid Build Coastguard Worker    pwd = None
55*cda5da8dSAndroid Build Coastguard Workertry:
56*cda5da8dSAndroid Build Coastguard Worker    import grp
57*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
58*cda5da8dSAndroid Build Coastguard Worker    grp = None
59*cda5da8dSAndroid Build Coastguard Worker
60*cda5da8dSAndroid Build Coastguard Worker# os.symlink on Windows prior to 6.0 raises NotImplementedError
61*cda5da8dSAndroid Build Coastguard Workersymlink_exception = (AttributeError, NotImplementedError)
62*cda5da8dSAndroid Build Coastguard Workertry:
63*cda5da8dSAndroid Build Coastguard Worker    # OSError (winerror=1314) will be raised if the caller does not hold the
64*cda5da8dSAndroid Build Coastguard Worker    # SeCreateSymbolicLinkPrivilege privilege
65*cda5da8dSAndroid Build Coastguard Worker    symlink_exception += (OSError,)
66*cda5da8dSAndroid Build Coastguard Workerexcept NameError:
67*cda5da8dSAndroid Build Coastguard Worker    pass
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard Worker# from tarfile import *
70*cda5da8dSAndroid Build Coastguard Worker__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError",
71*cda5da8dSAndroid Build Coastguard Worker           "CompressionError", "StreamError", "ExtractError", "HeaderError",
72*cda5da8dSAndroid Build Coastguard Worker           "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT",
73*cda5da8dSAndroid Build Coastguard Worker           "DEFAULT_FORMAT", "open"]
74*cda5da8dSAndroid Build Coastguard Worker
75*cda5da8dSAndroid Build Coastguard Worker
76*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
77*cda5da8dSAndroid Build Coastguard Worker# tar constants
78*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
79*cda5da8dSAndroid Build Coastguard WorkerNUL = b"\0"                     # the null character
80*cda5da8dSAndroid Build Coastguard WorkerBLOCKSIZE = 512                 # length of processing blocks
81*cda5da8dSAndroid Build Coastguard WorkerRECORDSIZE = BLOCKSIZE * 20     # length of records
82*cda5da8dSAndroid Build Coastguard WorkerGNU_MAGIC = b"ustar  \0"        # magic gnu tar string
83*cda5da8dSAndroid Build Coastguard WorkerPOSIX_MAGIC = b"ustar\x0000"    # magic posix tar string
84*cda5da8dSAndroid Build Coastguard Worker
85*cda5da8dSAndroid Build Coastguard WorkerLENGTH_NAME = 100               # maximum length of a filename
86*cda5da8dSAndroid Build Coastguard WorkerLENGTH_LINK = 100               # maximum length of a linkname
87*cda5da8dSAndroid Build Coastguard WorkerLENGTH_PREFIX = 155             # maximum length of the prefix field
88*cda5da8dSAndroid Build Coastguard Worker
89*cda5da8dSAndroid Build Coastguard WorkerREGTYPE = b"0"                  # regular file
90*cda5da8dSAndroid Build Coastguard WorkerAREGTYPE = b"\0"                # regular file
91*cda5da8dSAndroid Build Coastguard WorkerLNKTYPE = b"1"                  # link (inside tarfile)
92*cda5da8dSAndroid Build Coastguard WorkerSYMTYPE = b"2"                  # symbolic link
93*cda5da8dSAndroid Build Coastguard WorkerCHRTYPE = b"3"                  # character special device
94*cda5da8dSAndroid Build Coastguard WorkerBLKTYPE = b"4"                  # block special device
95*cda5da8dSAndroid Build Coastguard WorkerDIRTYPE = b"5"                  # directory
96*cda5da8dSAndroid Build Coastguard WorkerFIFOTYPE = b"6"                 # fifo special device
97*cda5da8dSAndroid Build Coastguard WorkerCONTTYPE = b"7"                 # contiguous file
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard WorkerGNUTYPE_LONGNAME = b"L"         # GNU tar longname
100*cda5da8dSAndroid Build Coastguard WorkerGNUTYPE_LONGLINK = b"K"         # GNU tar longlink
101*cda5da8dSAndroid Build Coastguard WorkerGNUTYPE_SPARSE = b"S"           # GNU tar sparse file
102*cda5da8dSAndroid Build Coastguard Worker
103*cda5da8dSAndroid Build Coastguard WorkerXHDTYPE = b"x"                  # POSIX.1-2001 extended header
104*cda5da8dSAndroid Build Coastguard WorkerXGLTYPE = b"g"                  # POSIX.1-2001 global header
105*cda5da8dSAndroid Build Coastguard WorkerSOLARIS_XHDTYPE = b"X"          # Solaris extended header
106*cda5da8dSAndroid Build Coastguard Worker
107*cda5da8dSAndroid Build Coastguard WorkerUSTAR_FORMAT = 0                # POSIX.1-1988 (ustar) format
108*cda5da8dSAndroid Build Coastguard WorkerGNU_FORMAT = 1                  # GNU tar format
109*cda5da8dSAndroid Build Coastguard WorkerPAX_FORMAT = 2                  # POSIX.1-2001 (pax) format
110*cda5da8dSAndroid Build Coastguard WorkerDEFAULT_FORMAT = PAX_FORMAT
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
113*cda5da8dSAndroid Build Coastguard Worker# tarfile constants
114*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
115*cda5da8dSAndroid Build Coastguard Worker# File types that tarfile supports:
116*cda5da8dSAndroid Build Coastguard WorkerSUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE,
117*cda5da8dSAndroid Build Coastguard Worker                   SYMTYPE, DIRTYPE, FIFOTYPE,
118*cda5da8dSAndroid Build Coastguard Worker                   CONTTYPE, CHRTYPE, BLKTYPE,
119*cda5da8dSAndroid Build Coastguard Worker                   GNUTYPE_LONGNAME, GNUTYPE_LONGLINK,
120*cda5da8dSAndroid Build Coastguard Worker                   GNUTYPE_SPARSE)
121*cda5da8dSAndroid Build Coastguard Worker
122*cda5da8dSAndroid Build Coastguard Worker# File types that will be treated as a regular file.
123*cda5da8dSAndroid Build Coastguard WorkerREGULAR_TYPES = (REGTYPE, AREGTYPE,
124*cda5da8dSAndroid Build Coastguard Worker                 CONTTYPE, GNUTYPE_SPARSE)
125*cda5da8dSAndroid Build Coastguard Worker
126*cda5da8dSAndroid Build Coastguard Worker# File types that are part of the GNU tar format.
127*cda5da8dSAndroid Build Coastguard WorkerGNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK,
128*cda5da8dSAndroid Build Coastguard Worker             GNUTYPE_SPARSE)
129*cda5da8dSAndroid Build Coastguard Worker
130*cda5da8dSAndroid Build Coastguard Worker# Fields from a pax header that override a TarInfo attribute.
131*cda5da8dSAndroid Build Coastguard WorkerPAX_FIELDS = ("path", "linkpath", "size", "mtime",
132*cda5da8dSAndroid Build Coastguard Worker              "uid", "gid", "uname", "gname")
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker# Fields from a pax header that are affected by hdrcharset.
135*cda5da8dSAndroid Build Coastguard WorkerPAX_NAME_FIELDS = {"path", "linkpath", "uname", "gname"}
136*cda5da8dSAndroid Build Coastguard Worker
137*cda5da8dSAndroid Build Coastguard Worker# Fields in a pax header that are numbers, all other fields
138*cda5da8dSAndroid Build Coastguard Worker# are treated as strings.
139*cda5da8dSAndroid Build Coastguard WorkerPAX_NUMBER_FIELDS = {
140*cda5da8dSAndroid Build Coastguard Worker    "atime": float,
141*cda5da8dSAndroid Build Coastguard Worker    "ctime": float,
142*cda5da8dSAndroid Build Coastguard Worker    "mtime": float,
143*cda5da8dSAndroid Build Coastguard Worker    "uid": int,
144*cda5da8dSAndroid Build Coastguard Worker    "gid": int,
145*cda5da8dSAndroid Build Coastguard Worker    "size": int
146*cda5da8dSAndroid Build Coastguard Worker}
147*cda5da8dSAndroid Build Coastguard Worker
148*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
149*cda5da8dSAndroid Build Coastguard Worker# initialization
150*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
151*cda5da8dSAndroid Build Coastguard Workerif os.name == "nt":
152*cda5da8dSAndroid Build Coastguard Worker    ENCODING = "utf-8"
153*cda5da8dSAndroid Build Coastguard Workerelse:
154*cda5da8dSAndroid Build Coastguard Worker    ENCODING = sys.getfilesystemencoding()
155*cda5da8dSAndroid Build Coastguard Worker
156*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
157*cda5da8dSAndroid Build Coastguard Worker# Some useful functions
158*cda5da8dSAndroid Build Coastguard Worker#---------------------------------------------------------
159*cda5da8dSAndroid Build Coastguard Worker
160*cda5da8dSAndroid Build Coastguard Workerdef stn(s, length, encoding, errors):
161*cda5da8dSAndroid Build Coastguard Worker    """Convert a string to a null-terminated bytes object.
162*cda5da8dSAndroid Build Coastguard Worker    """
163*cda5da8dSAndroid Build Coastguard Worker    if s is None:
164*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("metadata cannot contain None")
165*cda5da8dSAndroid Build Coastguard Worker    s = s.encode(encoding, errors)
166*cda5da8dSAndroid Build Coastguard Worker    return s[:length] + (length - len(s)) * NUL
167*cda5da8dSAndroid Build Coastguard Worker
168*cda5da8dSAndroid Build Coastguard Workerdef nts(s, encoding, errors):
169*cda5da8dSAndroid Build Coastguard Worker    """Convert a null-terminated bytes object to a string.
170*cda5da8dSAndroid Build Coastguard Worker    """
171*cda5da8dSAndroid Build Coastguard Worker    p = s.find(b"\0")
172*cda5da8dSAndroid Build Coastguard Worker    if p != -1:
173*cda5da8dSAndroid Build Coastguard Worker        s = s[:p]
174*cda5da8dSAndroid Build Coastguard Worker    return s.decode(encoding, errors)
175*cda5da8dSAndroid Build Coastguard Worker
176*cda5da8dSAndroid Build Coastguard Workerdef nti(s):
177*cda5da8dSAndroid Build Coastguard Worker    """Convert a number field to a python number.
178*cda5da8dSAndroid Build Coastguard Worker    """
179*cda5da8dSAndroid Build Coastguard Worker    # There are two possible encodings for a number field, see
180*cda5da8dSAndroid Build Coastguard Worker    # itn() below.
181*cda5da8dSAndroid Build Coastguard Worker    if s[0] in (0o200, 0o377):
182*cda5da8dSAndroid Build Coastguard Worker        n = 0
183*cda5da8dSAndroid Build Coastguard Worker        for i in range(len(s) - 1):
184*cda5da8dSAndroid Build Coastguard Worker            n <<= 8
185*cda5da8dSAndroid Build Coastguard Worker            n += s[i + 1]
186*cda5da8dSAndroid Build Coastguard Worker        if s[0] == 0o377:
187*cda5da8dSAndroid Build Coastguard Worker            n = -(256 ** (len(s) - 1) - n)
188*cda5da8dSAndroid Build Coastguard Worker    else:
189*cda5da8dSAndroid Build Coastguard Worker        try:
190*cda5da8dSAndroid Build Coastguard Worker            s = nts(s, "ascii", "strict")
191*cda5da8dSAndroid Build Coastguard Worker            n = int(s.strip() or "0", 8)
192*cda5da8dSAndroid Build Coastguard Worker        except ValueError:
193*cda5da8dSAndroid Build Coastguard Worker            raise InvalidHeaderError("invalid header")
194*cda5da8dSAndroid Build Coastguard Worker    return n
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Workerdef itn(n, digits=8, format=DEFAULT_FORMAT):
197*cda5da8dSAndroid Build Coastguard Worker    """Convert a python number to a number field.
198*cda5da8dSAndroid Build Coastguard Worker    """
199*cda5da8dSAndroid Build Coastguard Worker    # POSIX 1003.1-1988 requires numbers to be encoded as a string of
200*cda5da8dSAndroid Build Coastguard Worker    # octal digits followed by a null-byte, this allows values up to
201*cda5da8dSAndroid Build Coastguard Worker    # (8**(digits-1))-1. GNU tar allows storing numbers greater than
202*cda5da8dSAndroid Build Coastguard Worker    # that if necessary. A leading 0o200 or 0o377 byte indicate this
203*cda5da8dSAndroid Build Coastguard Worker    # particular encoding, the following digits-1 bytes are a big-endian
204*cda5da8dSAndroid Build Coastguard Worker    # base-256 representation. This allows values up to (256**(digits-1))-1.
205*cda5da8dSAndroid Build Coastguard Worker    # A 0o200 byte indicates a positive number, a 0o377 byte a negative
206*cda5da8dSAndroid Build Coastguard Worker    # number.
207*cda5da8dSAndroid Build Coastguard Worker    original_n = n
208*cda5da8dSAndroid Build Coastguard Worker    n = int(n)
209*cda5da8dSAndroid Build Coastguard Worker    if 0 <= n < 8 ** (digits - 1):
210*cda5da8dSAndroid Build Coastguard Worker        s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL
211*cda5da8dSAndroid Build Coastguard Worker    elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1):
212*cda5da8dSAndroid Build Coastguard Worker        if n >= 0:
213*cda5da8dSAndroid Build Coastguard Worker            s = bytearray([0o200])
214*cda5da8dSAndroid Build Coastguard Worker        else:
215*cda5da8dSAndroid Build Coastguard Worker            s = bytearray([0o377])
216*cda5da8dSAndroid Build Coastguard Worker            n = 256 ** digits + n
217*cda5da8dSAndroid Build Coastguard Worker
218*cda5da8dSAndroid Build Coastguard Worker        for i in range(digits - 1):
219*cda5da8dSAndroid Build Coastguard Worker            s.insert(1, n & 0o377)
220*cda5da8dSAndroid Build Coastguard Worker            n >>= 8
221*cda5da8dSAndroid Build Coastguard Worker    else:
222*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("overflow in number field")
223*cda5da8dSAndroid Build Coastguard Worker
224*cda5da8dSAndroid Build Coastguard Worker    return s
225*cda5da8dSAndroid Build Coastguard Worker
226*cda5da8dSAndroid Build Coastguard Workerdef calc_chksums(buf):
227*cda5da8dSAndroid Build Coastguard Worker    """Calculate the checksum for a member's header by summing up all
228*cda5da8dSAndroid Build Coastguard Worker       characters except for the chksum field which is treated as if
229*cda5da8dSAndroid Build Coastguard Worker       it was filled with spaces. According to the GNU tar sources,
230*cda5da8dSAndroid Build Coastguard Worker       some tars (Sun and NeXT) calculate chksum with signed char,
231*cda5da8dSAndroid Build Coastguard Worker       which will be different if there are chars in the buffer with
232*cda5da8dSAndroid Build Coastguard Worker       the high bit set. So we calculate two checksums, unsigned and
233*cda5da8dSAndroid Build Coastguard Worker       signed.
234*cda5da8dSAndroid Build Coastguard Worker    """
235*cda5da8dSAndroid Build Coastguard Worker    unsigned_chksum = 256 + sum(struct.unpack_from("148B8x356B", buf))
236*cda5da8dSAndroid Build Coastguard Worker    signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf))
237*cda5da8dSAndroid Build Coastguard Worker    return unsigned_chksum, signed_chksum
238*cda5da8dSAndroid Build Coastguard Worker
239*cda5da8dSAndroid Build Coastguard Workerdef copyfileobj(src, dst, length=None, exception=OSError, bufsize=None):
240*cda5da8dSAndroid Build Coastguard Worker    """Copy length bytes from fileobj src to fileobj dst.
241*cda5da8dSAndroid Build Coastguard Worker       If length is None, copy the entire content.
242*cda5da8dSAndroid Build Coastguard Worker    """
243*cda5da8dSAndroid Build Coastguard Worker    bufsize = bufsize or 16 * 1024
244*cda5da8dSAndroid Build Coastguard Worker    if length == 0:
245*cda5da8dSAndroid Build Coastguard Worker        return
246*cda5da8dSAndroid Build Coastguard Worker    if length is None:
247*cda5da8dSAndroid Build Coastguard Worker        shutil.copyfileobj(src, dst, bufsize)
248*cda5da8dSAndroid Build Coastguard Worker        return
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker    blocks, remainder = divmod(length, bufsize)
251*cda5da8dSAndroid Build Coastguard Worker    for b in range(blocks):
252*cda5da8dSAndroid Build Coastguard Worker        buf = src.read(bufsize)
253*cda5da8dSAndroid Build Coastguard Worker        if len(buf) < bufsize:
254*cda5da8dSAndroid Build Coastguard Worker            raise exception("unexpected end of data")
255*cda5da8dSAndroid Build Coastguard Worker        dst.write(buf)
256*cda5da8dSAndroid Build Coastguard Worker
257*cda5da8dSAndroid Build Coastguard Worker    if remainder != 0:
258*cda5da8dSAndroid Build Coastguard Worker        buf = src.read(remainder)
259*cda5da8dSAndroid Build Coastguard Worker        if len(buf) < remainder:
260*cda5da8dSAndroid Build Coastguard Worker            raise exception("unexpected end of data")
261*cda5da8dSAndroid Build Coastguard Worker        dst.write(buf)
262*cda5da8dSAndroid Build Coastguard Worker    return
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Workerdef _safe_print(s):
265*cda5da8dSAndroid Build Coastguard Worker    encoding = getattr(sys.stdout, 'encoding', None)
266*cda5da8dSAndroid Build Coastguard Worker    if encoding is not None:
267*cda5da8dSAndroid Build Coastguard Worker        s = s.encode(encoding, 'backslashreplace').decode(encoding)
268*cda5da8dSAndroid Build Coastguard Worker    print(s, end=' ')
269*cda5da8dSAndroid Build Coastguard Worker
270*cda5da8dSAndroid Build Coastguard Worker
271*cda5da8dSAndroid Build Coastguard Workerclass TarError(Exception):
272*cda5da8dSAndroid Build Coastguard Worker    """Base exception."""
273*cda5da8dSAndroid Build Coastguard Worker    pass
274*cda5da8dSAndroid Build Coastguard Workerclass ExtractError(TarError):
275*cda5da8dSAndroid Build Coastguard Worker    """General exception for extract errors."""
276*cda5da8dSAndroid Build Coastguard Worker    pass
277*cda5da8dSAndroid Build Coastguard Workerclass ReadError(TarError):
278*cda5da8dSAndroid Build Coastguard Worker    """Exception for unreadable tar archives."""
279*cda5da8dSAndroid Build Coastguard Worker    pass
280*cda5da8dSAndroid Build Coastguard Workerclass CompressionError(TarError):
281*cda5da8dSAndroid Build Coastguard Worker    """Exception for unavailable compression methods."""
282*cda5da8dSAndroid Build Coastguard Worker    pass
283*cda5da8dSAndroid Build Coastguard Workerclass StreamError(TarError):
284*cda5da8dSAndroid Build Coastguard Worker    """Exception for unsupported operations on stream-like TarFiles."""
285*cda5da8dSAndroid Build Coastguard Worker    pass
286*cda5da8dSAndroid Build Coastguard Workerclass HeaderError(TarError):
287*cda5da8dSAndroid Build Coastguard Worker    """Base exception for header errors."""
288*cda5da8dSAndroid Build Coastguard Worker    pass
289*cda5da8dSAndroid Build Coastguard Workerclass EmptyHeaderError(HeaderError):
290*cda5da8dSAndroid Build Coastguard Worker    """Exception for empty headers."""
291*cda5da8dSAndroid Build Coastguard Worker    pass
292*cda5da8dSAndroid Build Coastguard Workerclass TruncatedHeaderError(HeaderError):
293*cda5da8dSAndroid Build Coastguard Worker    """Exception for truncated headers."""
294*cda5da8dSAndroid Build Coastguard Worker    pass
295*cda5da8dSAndroid Build Coastguard Workerclass EOFHeaderError(HeaderError):
296*cda5da8dSAndroid Build Coastguard Worker    """Exception for end of file headers."""
297*cda5da8dSAndroid Build Coastguard Worker    pass
298*cda5da8dSAndroid Build Coastguard Workerclass InvalidHeaderError(HeaderError):
299*cda5da8dSAndroid Build Coastguard Worker    """Exception for invalid headers."""
300*cda5da8dSAndroid Build Coastguard Worker    pass
301*cda5da8dSAndroid Build Coastguard Workerclass SubsequentHeaderError(HeaderError):
302*cda5da8dSAndroid Build Coastguard Worker    """Exception for missing and invalid extended headers."""
303*cda5da8dSAndroid Build Coastguard Worker    pass
304*cda5da8dSAndroid Build Coastguard Worker
305*cda5da8dSAndroid Build Coastguard Worker#---------------------------
306*cda5da8dSAndroid Build Coastguard Worker# internal stream interface
307*cda5da8dSAndroid Build Coastguard Worker#---------------------------
308*cda5da8dSAndroid Build Coastguard Workerclass _LowLevelFile:
309*cda5da8dSAndroid Build Coastguard Worker    """Low-level file object. Supports reading and writing.
310*cda5da8dSAndroid Build Coastguard Worker       It is used instead of a regular file object for streaming
311*cda5da8dSAndroid Build Coastguard Worker       access.
312*cda5da8dSAndroid Build Coastguard Worker    """
313*cda5da8dSAndroid Build Coastguard Worker
314*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, name, mode):
315*cda5da8dSAndroid Build Coastguard Worker        mode = {
316*cda5da8dSAndroid Build Coastguard Worker            "r": os.O_RDONLY,
317*cda5da8dSAndroid Build Coastguard Worker            "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
318*cda5da8dSAndroid Build Coastguard Worker        }[mode]
319*cda5da8dSAndroid Build Coastguard Worker        if hasattr(os, "O_BINARY"):
320*cda5da8dSAndroid Build Coastguard Worker            mode |= os.O_BINARY
321*cda5da8dSAndroid Build Coastguard Worker        self.fd = os.open(name, mode, 0o666)
322*cda5da8dSAndroid Build Coastguard Worker
323*cda5da8dSAndroid Build Coastguard Worker    def close(self):
324*cda5da8dSAndroid Build Coastguard Worker        os.close(self.fd)
325*cda5da8dSAndroid Build Coastguard Worker
326*cda5da8dSAndroid Build Coastguard Worker    def read(self, size):
327*cda5da8dSAndroid Build Coastguard Worker        return os.read(self.fd, size)
328*cda5da8dSAndroid Build Coastguard Worker
329*cda5da8dSAndroid Build Coastguard Worker    def write(self, s):
330*cda5da8dSAndroid Build Coastguard Worker        os.write(self.fd, s)
331*cda5da8dSAndroid Build Coastguard Worker
332*cda5da8dSAndroid Build Coastguard Workerclass _Stream:
333*cda5da8dSAndroid Build Coastguard Worker    """Class that serves as an adapter between TarFile and
334*cda5da8dSAndroid Build Coastguard Worker       a stream-like object.  The stream-like object only
335*cda5da8dSAndroid Build Coastguard Worker       needs to have a read() or write() method and is accessed
336*cda5da8dSAndroid Build Coastguard Worker       blockwise.  Use of gzip or bzip2 compression is possible.
337*cda5da8dSAndroid Build Coastguard Worker       A stream-like object could be for example: sys.stdin,
338*cda5da8dSAndroid Build Coastguard Worker       sys.stdout, a socket, a tape device etc.
339*cda5da8dSAndroid Build Coastguard Worker
340*cda5da8dSAndroid Build Coastguard Worker       _Stream is intended to be used only internally.
341*cda5da8dSAndroid Build Coastguard Worker    """
342*cda5da8dSAndroid Build Coastguard Worker
343*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, name, mode, comptype, fileobj, bufsize):
344*cda5da8dSAndroid Build Coastguard Worker        """Construct a _Stream object.
345*cda5da8dSAndroid Build Coastguard Worker        """
346*cda5da8dSAndroid Build Coastguard Worker        self._extfileobj = True
347*cda5da8dSAndroid Build Coastguard Worker        if fileobj is None:
348*cda5da8dSAndroid Build Coastguard Worker            fileobj = _LowLevelFile(name, mode)
349*cda5da8dSAndroid Build Coastguard Worker            self._extfileobj = False
350*cda5da8dSAndroid Build Coastguard Worker
351*cda5da8dSAndroid Build Coastguard Worker        if comptype == '*':
352*cda5da8dSAndroid Build Coastguard Worker            # Enable transparent compression detection for the
353*cda5da8dSAndroid Build Coastguard Worker            # stream interface
354*cda5da8dSAndroid Build Coastguard Worker            fileobj = _StreamProxy(fileobj)
355*cda5da8dSAndroid Build Coastguard Worker            comptype = fileobj.getcomptype()
356*cda5da8dSAndroid Build Coastguard Worker
357*cda5da8dSAndroid Build Coastguard Worker        self.name     = name or ""
358*cda5da8dSAndroid Build Coastguard Worker        self.mode     = mode
359*cda5da8dSAndroid Build Coastguard Worker        self.comptype = comptype
360*cda5da8dSAndroid Build Coastguard Worker        self.fileobj  = fileobj
361*cda5da8dSAndroid Build Coastguard Worker        self.bufsize  = bufsize
362*cda5da8dSAndroid Build Coastguard Worker        self.buf      = b""
363*cda5da8dSAndroid Build Coastguard Worker        self.pos      = 0
364*cda5da8dSAndroid Build Coastguard Worker        self.closed   = False
365*cda5da8dSAndroid Build Coastguard Worker
366*cda5da8dSAndroid Build Coastguard Worker        try:
367*cda5da8dSAndroid Build Coastguard Worker            if comptype == "gz":
368*cda5da8dSAndroid Build Coastguard Worker                try:
369*cda5da8dSAndroid Build Coastguard Worker                    import zlib
370*cda5da8dSAndroid Build Coastguard Worker                except ImportError:
371*cda5da8dSAndroid Build Coastguard Worker                    raise CompressionError("zlib module is not available") from None
372*cda5da8dSAndroid Build Coastguard Worker                self.zlib = zlib
373*cda5da8dSAndroid Build Coastguard Worker                self.crc = zlib.crc32(b"")
374*cda5da8dSAndroid Build Coastguard Worker                if mode == "r":
375*cda5da8dSAndroid Build Coastguard Worker                    self._init_read_gz()
376*cda5da8dSAndroid Build Coastguard Worker                    self.exception = zlib.error
377*cda5da8dSAndroid Build Coastguard Worker                else:
378*cda5da8dSAndroid Build Coastguard Worker                    self._init_write_gz()
379*cda5da8dSAndroid Build Coastguard Worker
380*cda5da8dSAndroid Build Coastguard Worker            elif comptype == "bz2":
381*cda5da8dSAndroid Build Coastguard Worker                try:
382*cda5da8dSAndroid Build Coastguard Worker                    import bz2
383*cda5da8dSAndroid Build Coastguard Worker                except ImportError:
384*cda5da8dSAndroid Build Coastguard Worker                    raise CompressionError("bz2 module is not available") from None
385*cda5da8dSAndroid Build Coastguard Worker                if mode == "r":
386*cda5da8dSAndroid Build Coastguard Worker                    self.dbuf = b""
387*cda5da8dSAndroid Build Coastguard Worker                    self.cmp = bz2.BZ2Decompressor()
388*cda5da8dSAndroid Build Coastguard Worker                    self.exception = OSError
389*cda5da8dSAndroid Build Coastguard Worker                else:
390*cda5da8dSAndroid Build Coastguard Worker                    self.cmp = bz2.BZ2Compressor()
391*cda5da8dSAndroid Build Coastguard Worker
392*cda5da8dSAndroid Build Coastguard Worker            elif comptype == "xz":
393*cda5da8dSAndroid Build Coastguard Worker                try:
394*cda5da8dSAndroid Build Coastguard Worker                    import lzma
395*cda5da8dSAndroid Build Coastguard Worker                except ImportError:
396*cda5da8dSAndroid Build Coastguard Worker                    raise CompressionError("lzma module is not available") from None
397*cda5da8dSAndroid Build Coastguard Worker                if mode == "r":
398*cda5da8dSAndroid Build Coastguard Worker                    self.dbuf = b""
399*cda5da8dSAndroid Build Coastguard Worker                    self.cmp = lzma.LZMADecompressor()
400*cda5da8dSAndroid Build Coastguard Worker                    self.exception = lzma.LZMAError
401*cda5da8dSAndroid Build Coastguard Worker                else:
402*cda5da8dSAndroid Build Coastguard Worker                    self.cmp = lzma.LZMACompressor()
403*cda5da8dSAndroid Build Coastguard Worker
404*cda5da8dSAndroid Build Coastguard Worker            elif comptype != "tar":
405*cda5da8dSAndroid Build Coastguard Worker                raise CompressionError("unknown compression type %r" % comptype)
406*cda5da8dSAndroid Build Coastguard Worker
407*cda5da8dSAndroid Build Coastguard Worker        except:
408*cda5da8dSAndroid Build Coastguard Worker            if not self._extfileobj:
409*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.close()
410*cda5da8dSAndroid Build Coastguard Worker            self.closed = True
411*cda5da8dSAndroid Build Coastguard Worker            raise
412*cda5da8dSAndroid Build Coastguard Worker
413*cda5da8dSAndroid Build Coastguard Worker    def __del__(self):
414*cda5da8dSAndroid Build Coastguard Worker        if hasattr(self, "closed") and not self.closed:
415*cda5da8dSAndroid Build Coastguard Worker            self.close()
416*cda5da8dSAndroid Build Coastguard Worker
417*cda5da8dSAndroid Build Coastguard Worker    def _init_write_gz(self):
418*cda5da8dSAndroid Build Coastguard Worker        """Initialize for writing with gzip compression.
419*cda5da8dSAndroid Build Coastguard Worker        """
420*cda5da8dSAndroid Build Coastguard Worker        self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED,
421*cda5da8dSAndroid Build Coastguard Worker                                            -self.zlib.MAX_WBITS,
422*cda5da8dSAndroid Build Coastguard Worker                                            self.zlib.DEF_MEM_LEVEL,
423*cda5da8dSAndroid Build Coastguard Worker                                            0)
424*cda5da8dSAndroid Build Coastguard Worker        timestamp = struct.pack("<L", int(time.time()))
425*cda5da8dSAndroid Build Coastguard Worker        self.__write(b"\037\213\010\010" + timestamp + b"\002\377")
426*cda5da8dSAndroid Build Coastguard Worker        if self.name.endswith(".gz"):
427*cda5da8dSAndroid Build Coastguard Worker            self.name = self.name[:-3]
428*cda5da8dSAndroid Build Coastguard Worker        # Honor "directory components removed" from RFC1952
429*cda5da8dSAndroid Build Coastguard Worker        self.name = os.path.basename(self.name)
430*cda5da8dSAndroid Build Coastguard Worker        # RFC1952 says we must use ISO-8859-1 for the FNAME field.
431*cda5da8dSAndroid Build Coastguard Worker        self.__write(self.name.encode("iso-8859-1", "replace") + NUL)
432*cda5da8dSAndroid Build Coastguard Worker
433*cda5da8dSAndroid Build Coastguard Worker    def write(self, s):
434*cda5da8dSAndroid Build Coastguard Worker        """Write string s to the stream.
435*cda5da8dSAndroid Build Coastguard Worker        """
436*cda5da8dSAndroid Build Coastguard Worker        if self.comptype == "gz":
437*cda5da8dSAndroid Build Coastguard Worker            self.crc = self.zlib.crc32(s, self.crc)
438*cda5da8dSAndroid Build Coastguard Worker        self.pos += len(s)
439*cda5da8dSAndroid Build Coastguard Worker        if self.comptype != "tar":
440*cda5da8dSAndroid Build Coastguard Worker            s = self.cmp.compress(s)
441*cda5da8dSAndroid Build Coastguard Worker        self.__write(s)
442*cda5da8dSAndroid Build Coastguard Worker
443*cda5da8dSAndroid Build Coastguard Worker    def __write(self, s):
444*cda5da8dSAndroid Build Coastguard Worker        """Write string s to the stream if a whole new block
445*cda5da8dSAndroid Build Coastguard Worker           is ready to be written.
446*cda5da8dSAndroid Build Coastguard Worker        """
447*cda5da8dSAndroid Build Coastguard Worker        self.buf += s
448*cda5da8dSAndroid Build Coastguard Worker        while len(self.buf) > self.bufsize:
449*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.write(self.buf[:self.bufsize])
450*cda5da8dSAndroid Build Coastguard Worker            self.buf = self.buf[self.bufsize:]
451*cda5da8dSAndroid Build Coastguard Worker
452*cda5da8dSAndroid Build Coastguard Worker    def close(self):
453*cda5da8dSAndroid Build Coastguard Worker        """Close the _Stream object. No operation should be
454*cda5da8dSAndroid Build Coastguard Worker           done on it afterwards.
455*cda5da8dSAndroid Build Coastguard Worker        """
456*cda5da8dSAndroid Build Coastguard Worker        if self.closed:
457*cda5da8dSAndroid Build Coastguard Worker            return
458*cda5da8dSAndroid Build Coastguard Worker
459*cda5da8dSAndroid Build Coastguard Worker        self.closed = True
460*cda5da8dSAndroid Build Coastguard Worker        try:
461*cda5da8dSAndroid Build Coastguard Worker            if self.mode == "w" and self.comptype != "tar":
462*cda5da8dSAndroid Build Coastguard Worker                self.buf += self.cmp.flush()
463*cda5da8dSAndroid Build Coastguard Worker
464*cda5da8dSAndroid Build Coastguard Worker            if self.mode == "w" and self.buf:
465*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.write(self.buf)
466*cda5da8dSAndroid Build Coastguard Worker                self.buf = b""
467*cda5da8dSAndroid Build Coastguard Worker                if self.comptype == "gz":
468*cda5da8dSAndroid Build Coastguard Worker                    self.fileobj.write(struct.pack("<L", self.crc))
469*cda5da8dSAndroid Build Coastguard Worker                    self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF))
470*cda5da8dSAndroid Build Coastguard Worker        finally:
471*cda5da8dSAndroid Build Coastguard Worker            if not self._extfileobj:
472*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.close()
473*cda5da8dSAndroid Build Coastguard Worker
474*cda5da8dSAndroid Build Coastguard Worker    def _init_read_gz(self):
475*cda5da8dSAndroid Build Coastguard Worker        """Initialize for reading a gzip compressed fileobj.
476*cda5da8dSAndroid Build Coastguard Worker        """
477*cda5da8dSAndroid Build Coastguard Worker        self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS)
478*cda5da8dSAndroid Build Coastguard Worker        self.dbuf = b""
479*cda5da8dSAndroid Build Coastguard Worker
480*cda5da8dSAndroid Build Coastguard Worker        # taken from gzip.GzipFile with some alterations
481*cda5da8dSAndroid Build Coastguard Worker        if self.__read(2) != b"\037\213":
482*cda5da8dSAndroid Build Coastguard Worker            raise ReadError("not a gzip file")
483*cda5da8dSAndroid Build Coastguard Worker        if self.__read(1) != b"\010":
484*cda5da8dSAndroid Build Coastguard Worker            raise CompressionError("unsupported compression method")
485*cda5da8dSAndroid Build Coastguard Worker
486*cda5da8dSAndroid Build Coastguard Worker        flag = ord(self.__read(1))
487*cda5da8dSAndroid Build Coastguard Worker        self.__read(6)
488*cda5da8dSAndroid Build Coastguard Worker
489*cda5da8dSAndroid Build Coastguard Worker        if flag & 4:
490*cda5da8dSAndroid Build Coastguard Worker            xlen = ord(self.__read(1)) + 256 * ord(self.__read(1))
491*cda5da8dSAndroid Build Coastguard Worker            self.read(xlen)
492*cda5da8dSAndroid Build Coastguard Worker        if flag & 8:
493*cda5da8dSAndroid Build Coastguard Worker            while True:
494*cda5da8dSAndroid Build Coastguard Worker                s = self.__read(1)
495*cda5da8dSAndroid Build Coastguard Worker                if not s or s == NUL:
496*cda5da8dSAndroid Build Coastguard Worker                    break
497*cda5da8dSAndroid Build Coastguard Worker        if flag & 16:
498*cda5da8dSAndroid Build Coastguard Worker            while True:
499*cda5da8dSAndroid Build Coastguard Worker                s = self.__read(1)
500*cda5da8dSAndroid Build Coastguard Worker                if not s or s == NUL:
501*cda5da8dSAndroid Build Coastguard Worker                    break
502*cda5da8dSAndroid Build Coastguard Worker        if flag & 2:
503*cda5da8dSAndroid Build Coastguard Worker            self.__read(2)
504*cda5da8dSAndroid Build Coastguard Worker
505*cda5da8dSAndroid Build Coastguard Worker    def tell(self):
506*cda5da8dSAndroid Build Coastguard Worker        """Return the stream's file pointer position.
507*cda5da8dSAndroid Build Coastguard Worker        """
508*cda5da8dSAndroid Build Coastguard Worker        return self.pos
509*cda5da8dSAndroid Build Coastguard Worker
510*cda5da8dSAndroid Build Coastguard Worker    def seek(self, pos=0):
511*cda5da8dSAndroid Build Coastguard Worker        """Set the stream's file pointer to pos. Negative seeking
512*cda5da8dSAndroid Build Coastguard Worker           is forbidden.
513*cda5da8dSAndroid Build Coastguard Worker        """
514*cda5da8dSAndroid Build Coastguard Worker        if pos - self.pos >= 0:
515*cda5da8dSAndroid Build Coastguard Worker            blocks, remainder = divmod(pos - self.pos, self.bufsize)
516*cda5da8dSAndroid Build Coastguard Worker            for i in range(blocks):
517*cda5da8dSAndroid Build Coastguard Worker                self.read(self.bufsize)
518*cda5da8dSAndroid Build Coastguard Worker            self.read(remainder)
519*cda5da8dSAndroid Build Coastguard Worker        else:
520*cda5da8dSAndroid Build Coastguard Worker            raise StreamError("seeking backwards is not allowed")
521*cda5da8dSAndroid Build Coastguard Worker        return self.pos
522*cda5da8dSAndroid Build Coastguard Worker
523*cda5da8dSAndroid Build Coastguard Worker    def read(self, size):
524*cda5da8dSAndroid Build Coastguard Worker        """Return the next size number of bytes from the stream."""
525*cda5da8dSAndroid Build Coastguard Worker        assert size is not None
526*cda5da8dSAndroid Build Coastguard Worker        buf = self._read(size)
527*cda5da8dSAndroid Build Coastguard Worker        self.pos += len(buf)
528*cda5da8dSAndroid Build Coastguard Worker        return buf
529*cda5da8dSAndroid Build Coastguard Worker
530*cda5da8dSAndroid Build Coastguard Worker    def _read(self, size):
531*cda5da8dSAndroid Build Coastguard Worker        """Return size bytes from the stream.
532*cda5da8dSAndroid Build Coastguard Worker        """
533*cda5da8dSAndroid Build Coastguard Worker        if self.comptype == "tar":
534*cda5da8dSAndroid Build Coastguard Worker            return self.__read(size)
535*cda5da8dSAndroid Build Coastguard Worker
536*cda5da8dSAndroid Build Coastguard Worker        c = len(self.dbuf)
537*cda5da8dSAndroid Build Coastguard Worker        t = [self.dbuf]
538*cda5da8dSAndroid Build Coastguard Worker        while c < size:
539*cda5da8dSAndroid Build Coastguard Worker            # Skip underlying buffer to avoid unaligned double buffering.
540*cda5da8dSAndroid Build Coastguard Worker            if self.buf:
541*cda5da8dSAndroid Build Coastguard Worker                buf = self.buf
542*cda5da8dSAndroid Build Coastguard Worker                self.buf = b""
543*cda5da8dSAndroid Build Coastguard Worker            else:
544*cda5da8dSAndroid Build Coastguard Worker                buf = self.fileobj.read(self.bufsize)
545*cda5da8dSAndroid Build Coastguard Worker                if not buf:
546*cda5da8dSAndroid Build Coastguard Worker                    break
547*cda5da8dSAndroid Build Coastguard Worker            try:
548*cda5da8dSAndroid Build Coastguard Worker                buf = self.cmp.decompress(buf)
549*cda5da8dSAndroid Build Coastguard Worker            except self.exception as e:
550*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("invalid compressed data") from e
551*cda5da8dSAndroid Build Coastguard Worker            t.append(buf)
552*cda5da8dSAndroid Build Coastguard Worker            c += len(buf)
553*cda5da8dSAndroid Build Coastguard Worker        t = b"".join(t)
554*cda5da8dSAndroid Build Coastguard Worker        self.dbuf = t[size:]
555*cda5da8dSAndroid Build Coastguard Worker        return t[:size]
556*cda5da8dSAndroid Build Coastguard Worker
557*cda5da8dSAndroid Build Coastguard Worker    def __read(self, size):
558*cda5da8dSAndroid Build Coastguard Worker        """Return size bytes from stream. If internal buffer is empty,
559*cda5da8dSAndroid Build Coastguard Worker           read another block from the stream.
560*cda5da8dSAndroid Build Coastguard Worker        """
561*cda5da8dSAndroid Build Coastguard Worker        c = len(self.buf)
562*cda5da8dSAndroid Build Coastguard Worker        t = [self.buf]
563*cda5da8dSAndroid Build Coastguard Worker        while c < size:
564*cda5da8dSAndroid Build Coastguard Worker            buf = self.fileobj.read(self.bufsize)
565*cda5da8dSAndroid Build Coastguard Worker            if not buf:
566*cda5da8dSAndroid Build Coastguard Worker                break
567*cda5da8dSAndroid Build Coastguard Worker            t.append(buf)
568*cda5da8dSAndroid Build Coastguard Worker            c += len(buf)
569*cda5da8dSAndroid Build Coastguard Worker        t = b"".join(t)
570*cda5da8dSAndroid Build Coastguard Worker        self.buf = t[size:]
571*cda5da8dSAndroid Build Coastguard Worker        return t[:size]
572*cda5da8dSAndroid Build Coastguard Worker# class _Stream
573*cda5da8dSAndroid Build Coastguard Worker
574*cda5da8dSAndroid Build Coastguard Workerclass _StreamProxy(object):
575*cda5da8dSAndroid Build Coastguard Worker    """Small proxy class that enables transparent compression
576*cda5da8dSAndroid Build Coastguard Worker       detection for the Stream interface (mode 'r|*').
577*cda5da8dSAndroid Build Coastguard Worker    """
578*cda5da8dSAndroid Build Coastguard Worker
579*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, fileobj):
580*cda5da8dSAndroid Build Coastguard Worker        self.fileobj = fileobj
581*cda5da8dSAndroid Build Coastguard Worker        self.buf = self.fileobj.read(BLOCKSIZE)
582*cda5da8dSAndroid Build Coastguard Worker
583*cda5da8dSAndroid Build Coastguard Worker    def read(self, size):
584*cda5da8dSAndroid Build Coastguard Worker        self.read = self.fileobj.read
585*cda5da8dSAndroid Build Coastguard Worker        return self.buf
586*cda5da8dSAndroid Build Coastguard Worker
587*cda5da8dSAndroid Build Coastguard Worker    def getcomptype(self):
588*cda5da8dSAndroid Build Coastguard Worker        if self.buf.startswith(b"\x1f\x8b\x08"):
589*cda5da8dSAndroid Build Coastguard Worker            return "gz"
590*cda5da8dSAndroid Build Coastguard Worker        elif self.buf[0:3] == b"BZh" and self.buf[4:10] == b"1AY&SY":
591*cda5da8dSAndroid Build Coastguard Worker            return "bz2"
592*cda5da8dSAndroid Build Coastguard Worker        elif self.buf.startswith((b"\x5d\x00\x00\x80", b"\xfd7zXZ")):
593*cda5da8dSAndroid Build Coastguard Worker            return "xz"
594*cda5da8dSAndroid Build Coastguard Worker        else:
595*cda5da8dSAndroid Build Coastguard Worker            return "tar"
596*cda5da8dSAndroid Build Coastguard Worker
597*cda5da8dSAndroid Build Coastguard Worker    def close(self):
598*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.close()
599*cda5da8dSAndroid Build Coastguard Worker# class StreamProxy
600*cda5da8dSAndroid Build Coastguard Worker
601*cda5da8dSAndroid Build Coastguard Worker#------------------------
602*cda5da8dSAndroid Build Coastguard Worker# Extraction file object
603*cda5da8dSAndroid Build Coastguard Worker#------------------------
604*cda5da8dSAndroid Build Coastguard Workerclass _FileInFile(object):
605*cda5da8dSAndroid Build Coastguard Worker    """A thin wrapper around an existing file object that
606*cda5da8dSAndroid Build Coastguard Worker       provides a part of its data as an individual file
607*cda5da8dSAndroid Build Coastguard Worker       object.
608*cda5da8dSAndroid Build Coastguard Worker    """
609*cda5da8dSAndroid Build Coastguard Worker
610*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, fileobj, offset, size, blockinfo=None):
611*cda5da8dSAndroid Build Coastguard Worker        self.fileobj = fileobj
612*cda5da8dSAndroid Build Coastguard Worker        self.offset = offset
613*cda5da8dSAndroid Build Coastguard Worker        self.size = size
614*cda5da8dSAndroid Build Coastguard Worker        self.position = 0
615*cda5da8dSAndroid Build Coastguard Worker        self.name = getattr(fileobj, "name", None)
616*cda5da8dSAndroid Build Coastguard Worker        self.closed = False
617*cda5da8dSAndroid Build Coastguard Worker
618*cda5da8dSAndroid Build Coastguard Worker        if blockinfo is None:
619*cda5da8dSAndroid Build Coastguard Worker            blockinfo = [(0, size)]
620*cda5da8dSAndroid Build Coastguard Worker
621*cda5da8dSAndroid Build Coastguard Worker        # Construct a map with data and zero blocks.
622*cda5da8dSAndroid Build Coastguard Worker        self.map_index = 0
623*cda5da8dSAndroid Build Coastguard Worker        self.map = []
624*cda5da8dSAndroid Build Coastguard Worker        lastpos = 0
625*cda5da8dSAndroid Build Coastguard Worker        realpos = self.offset
626*cda5da8dSAndroid Build Coastguard Worker        for offset, size in blockinfo:
627*cda5da8dSAndroid Build Coastguard Worker            if offset > lastpos:
628*cda5da8dSAndroid Build Coastguard Worker                self.map.append((False, lastpos, offset, None))
629*cda5da8dSAndroid Build Coastguard Worker            self.map.append((True, offset, offset + size, realpos))
630*cda5da8dSAndroid Build Coastguard Worker            realpos += size
631*cda5da8dSAndroid Build Coastguard Worker            lastpos = offset + size
632*cda5da8dSAndroid Build Coastguard Worker        if lastpos < self.size:
633*cda5da8dSAndroid Build Coastguard Worker            self.map.append((False, lastpos, self.size, None))
634*cda5da8dSAndroid Build Coastguard Worker
635*cda5da8dSAndroid Build Coastguard Worker    def flush(self):
636*cda5da8dSAndroid Build Coastguard Worker        pass
637*cda5da8dSAndroid Build Coastguard Worker
638*cda5da8dSAndroid Build Coastguard Worker    def readable(self):
639*cda5da8dSAndroid Build Coastguard Worker        return True
640*cda5da8dSAndroid Build Coastguard Worker
641*cda5da8dSAndroid Build Coastguard Worker    def writable(self):
642*cda5da8dSAndroid Build Coastguard Worker        return False
643*cda5da8dSAndroid Build Coastguard Worker
644*cda5da8dSAndroid Build Coastguard Worker    def seekable(self):
645*cda5da8dSAndroid Build Coastguard Worker        return self.fileobj.seekable()
646*cda5da8dSAndroid Build Coastguard Worker
647*cda5da8dSAndroid Build Coastguard Worker    def tell(self):
648*cda5da8dSAndroid Build Coastguard Worker        """Return the current file position.
649*cda5da8dSAndroid Build Coastguard Worker        """
650*cda5da8dSAndroid Build Coastguard Worker        return self.position
651*cda5da8dSAndroid Build Coastguard Worker
652*cda5da8dSAndroid Build Coastguard Worker    def seek(self, position, whence=io.SEEK_SET):
653*cda5da8dSAndroid Build Coastguard Worker        """Seek to a position in the file.
654*cda5da8dSAndroid Build Coastguard Worker        """
655*cda5da8dSAndroid Build Coastguard Worker        if whence == io.SEEK_SET:
656*cda5da8dSAndroid Build Coastguard Worker            self.position = min(max(position, 0), self.size)
657*cda5da8dSAndroid Build Coastguard Worker        elif whence == io.SEEK_CUR:
658*cda5da8dSAndroid Build Coastguard Worker            if position < 0:
659*cda5da8dSAndroid Build Coastguard Worker                self.position = max(self.position + position, 0)
660*cda5da8dSAndroid Build Coastguard Worker            else:
661*cda5da8dSAndroid Build Coastguard Worker                self.position = min(self.position + position, self.size)
662*cda5da8dSAndroid Build Coastguard Worker        elif whence == io.SEEK_END:
663*cda5da8dSAndroid Build Coastguard Worker            self.position = max(min(self.size + position, self.size), 0)
664*cda5da8dSAndroid Build Coastguard Worker        else:
665*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Invalid argument")
666*cda5da8dSAndroid Build Coastguard Worker        return self.position
667*cda5da8dSAndroid Build Coastguard Worker
668*cda5da8dSAndroid Build Coastguard Worker    def read(self, size=None):
669*cda5da8dSAndroid Build Coastguard Worker        """Read data from the file.
670*cda5da8dSAndroid Build Coastguard Worker        """
671*cda5da8dSAndroid Build Coastguard Worker        if size is None:
672*cda5da8dSAndroid Build Coastguard Worker            size = self.size - self.position
673*cda5da8dSAndroid Build Coastguard Worker        else:
674*cda5da8dSAndroid Build Coastguard Worker            size = min(size, self.size - self.position)
675*cda5da8dSAndroid Build Coastguard Worker
676*cda5da8dSAndroid Build Coastguard Worker        buf = b""
677*cda5da8dSAndroid Build Coastguard Worker        while size > 0:
678*cda5da8dSAndroid Build Coastguard Worker            while True:
679*cda5da8dSAndroid Build Coastguard Worker                data, start, stop, offset = self.map[self.map_index]
680*cda5da8dSAndroid Build Coastguard Worker                if start <= self.position < stop:
681*cda5da8dSAndroid Build Coastguard Worker                    break
682*cda5da8dSAndroid Build Coastguard Worker                else:
683*cda5da8dSAndroid Build Coastguard Worker                    self.map_index += 1
684*cda5da8dSAndroid Build Coastguard Worker                    if self.map_index == len(self.map):
685*cda5da8dSAndroid Build Coastguard Worker                        self.map_index = 0
686*cda5da8dSAndroid Build Coastguard Worker            length = min(size, stop - self.position)
687*cda5da8dSAndroid Build Coastguard Worker            if data:
688*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.seek(offset + (self.position - start))
689*cda5da8dSAndroid Build Coastguard Worker                b = self.fileobj.read(length)
690*cda5da8dSAndroid Build Coastguard Worker                if len(b) != length:
691*cda5da8dSAndroid Build Coastguard Worker                    raise ReadError("unexpected end of data")
692*cda5da8dSAndroid Build Coastguard Worker                buf += b
693*cda5da8dSAndroid Build Coastguard Worker            else:
694*cda5da8dSAndroid Build Coastguard Worker                buf += NUL * length
695*cda5da8dSAndroid Build Coastguard Worker            size -= length
696*cda5da8dSAndroid Build Coastguard Worker            self.position += length
697*cda5da8dSAndroid Build Coastguard Worker        return buf
698*cda5da8dSAndroid Build Coastguard Worker
699*cda5da8dSAndroid Build Coastguard Worker    def readinto(self, b):
700*cda5da8dSAndroid Build Coastguard Worker        buf = self.read(len(b))
701*cda5da8dSAndroid Build Coastguard Worker        b[:len(buf)] = buf
702*cda5da8dSAndroid Build Coastguard Worker        return len(buf)
703*cda5da8dSAndroid Build Coastguard Worker
704*cda5da8dSAndroid Build Coastguard Worker    def close(self):
705*cda5da8dSAndroid Build Coastguard Worker        self.closed = True
706*cda5da8dSAndroid Build Coastguard Worker#class _FileInFile
707*cda5da8dSAndroid Build Coastguard Worker
708*cda5da8dSAndroid Build Coastguard Workerclass ExFileObject(io.BufferedReader):
709*cda5da8dSAndroid Build Coastguard Worker
710*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarfile, tarinfo):
711*cda5da8dSAndroid Build Coastguard Worker        fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data,
712*cda5da8dSAndroid Build Coastguard Worker                tarinfo.size, tarinfo.sparse)
713*cda5da8dSAndroid Build Coastguard Worker        super().__init__(fileobj)
714*cda5da8dSAndroid Build Coastguard Worker#class ExFileObject
715*cda5da8dSAndroid Build Coastguard Worker
716*cda5da8dSAndroid Build Coastguard Worker
717*cda5da8dSAndroid Build Coastguard Worker#-----------------------------
718*cda5da8dSAndroid Build Coastguard Worker# extraction filters (PEP 706)
719*cda5da8dSAndroid Build Coastguard Worker#-----------------------------
720*cda5da8dSAndroid Build Coastguard Worker
721*cda5da8dSAndroid Build Coastguard Workerclass FilterError(TarError):
722*cda5da8dSAndroid Build Coastguard Worker    pass
723*cda5da8dSAndroid Build Coastguard Worker
724*cda5da8dSAndroid Build Coastguard Workerclass AbsolutePathError(FilterError):
725*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarinfo):
726*cda5da8dSAndroid Build Coastguard Worker        self.tarinfo = tarinfo
727*cda5da8dSAndroid Build Coastguard Worker        super().__init__(f'member {tarinfo.name!r} has an absolute path')
728*cda5da8dSAndroid Build Coastguard Worker
729*cda5da8dSAndroid Build Coastguard Workerclass OutsideDestinationError(FilterError):
730*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarinfo, path):
731*cda5da8dSAndroid Build Coastguard Worker        self.tarinfo = tarinfo
732*cda5da8dSAndroid Build Coastguard Worker        self._path = path
733*cda5da8dSAndroid Build Coastguard Worker        super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, '
734*cda5da8dSAndroid Build Coastguard Worker                         + 'which is outside the destination')
735*cda5da8dSAndroid Build Coastguard Worker
736*cda5da8dSAndroid Build Coastguard Workerclass SpecialFileError(FilterError):
737*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarinfo):
738*cda5da8dSAndroid Build Coastguard Worker        self.tarinfo = tarinfo
739*cda5da8dSAndroid Build Coastguard Worker        super().__init__(f'{tarinfo.name!r} is a special file')
740*cda5da8dSAndroid Build Coastguard Worker
741*cda5da8dSAndroid Build Coastguard Workerclass AbsoluteLinkError(FilterError):
742*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarinfo):
743*cda5da8dSAndroid Build Coastguard Worker        self.tarinfo = tarinfo
744*cda5da8dSAndroid Build Coastguard Worker        super().__init__(f'{tarinfo.name!r} is a symlink to an absolute path')
745*cda5da8dSAndroid Build Coastguard Worker
746*cda5da8dSAndroid Build Coastguard Workerclass LinkOutsideDestinationError(FilterError):
747*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, tarinfo, path):
748*cda5da8dSAndroid Build Coastguard Worker        self.tarinfo = tarinfo
749*cda5da8dSAndroid Build Coastguard Worker        self._path = path
750*cda5da8dSAndroid Build Coastguard Worker        super().__init__(f'{tarinfo.name!r} would link to {path!r}, '
751*cda5da8dSAndroid Build Coastguard Worker                         + 'which is outside the destination')
752*cda5da8dSAndroid Build Coastguard Worker
753*cda5da8dSAndroid Build Coastguard Workerdef _get_filtered_attrs(member, dest_path, for_data=True):
754*cda5da8dSAndroid Build Coastguard Worker    new_attrs = {}
755*cda5da8dSAndroid Build Coastguard Worker    name = member.name
756*cda5da8dSAndroid Build Coastguard Worker    dest_path = os.path.realpath(dest_path)
757*cda5da8dSAndroid Build Coastguard Worker    # Strip leading / (tar's directory separator) from filenames.
758*cda5da8dSAndroid Build Coastguard Worker    # Include os.sep (target OS directory separator) as well.
759*cda5da8dSAndroid Build Coastguard Worker    if name.startswith(('/', os.sep)):
760*cda5da8dSAndroid Build Coastguard Worker        name = new_attrs['name'] = member.path.lstrip('/' + os.sep)
761*cda5da8dSAndroid Build Coastguard Worker    if os.path.isabs(name):
762*cda5da8dSAndroid Build Coastguard Worker        # Path is absolute even after stripping.
763*cda5da8dSAndroid Build Coastguard Worker        # For example, 'C:/foo' on Windows.
764*cda5da8dSAndroid Build Coastguard Worker        raise AbsolutePathError(member)
765*cda5da8dSAndroid Build Coastguard Worker    # Ensure we stay in the destination
766*cda5da8dSAndroid Build Coastguard Worker    target_path = os.path.realpath(os.path.join(dest_path, name))
767*cda5da8dSAndroid Build Coastguard Worker    if os.path.commonpath([target_path, dest_path]) != dest_path:
768*cda5da8dSAndroid Build Coastguard Worker        raise OutsideDestinationError(member, target_path)
769*cda5da8dSAndroid Build Coastguard Worker    # Limit permissions (no high bits, and go-w)
770*cda5da8dSAndroid Build Coastguard Worker    mode = member.mode
771*cda5da8dSAndroid Build Coastguard Worker    if mode is not None:
772*cda5da8dSAndroid Build Coastguard Worker        # Strip high bits & group/other write bits
773*cda5da8dSAndroid Build Coastguard Worker        mode = mode & 0o755
774*cda5da8dSAndroid Build Coastguard Worker        if for_data:
775*cda5da8dSAndroid Build Coastguard Worker            # For data, handle permissions & file types
776*cda5da8dSAndroid Build Coastguard Worker            if member.isreg() or member.islnk():
777*cda5da8dSAndroid Build Coastguard Worker                if not mode & 0o100:
778*cda5da8dSAndroid Build Coastguard Worker                    # Clear executable bits if not executable by user
779*cda5da8dSAndroid Build Coastguard Worker                    mode &= ~0o111
780*cda5da8dSAndroid Build Coastguard Worker                # Ensure owner can read & write
781*cda5da8dSAndroid Build Coastguard Worker                mode |= 0o600
782*cda5da8dSAndroid Build Coastguard Worker            elif member.isdir() or member.issym():
783*cda5da8dSAndroid Build Coastguard Worker                # Ignore mode for directories & symlinks
784*cda5da8dSAndroid Build Coastguard Worker                mode = None
785*cda5da8dSAndroid Build Coastguard Worker            else:
786*cda5da8dSAndroid Build Coastguard Worker                # Reject special files
787*cda5da8dSAndroid Build Coastguard Worker                raise SpecialFileError(member)
788*cda5da8dSAndroid Build Coastguard Worker        if mode != member.mode:
789*cda5da8dSAndroid Build Coastguard Worker            new_attrs['mode'] = mode
790*cda5da8dSAndroid Build Coastguard Worker    if for_data:
791*cda5da8dSAndroid Build Coastguard Worker        # Ignore ownership for 'data'
792*cda5da8dSAndroid Build Coastguard Worker        if member.uid is not None:
793*cda5da8dSAndroid Build Coastguard Worker            new_attrs['uid'] = None
794*cda5da8dSAndroid Build Coastguard Worker        if member.gid is not None:
795*cda5da8dSAndroid Build Coastguard Worker            new_attrs['gid'] = None
796*cda5da8dSAndroid Build Coastguard Worker        if member.uname is not None:
797*cda5da8dSAndroid Build Coastguard Worker            new_attrs['uname'] = None
798*cda5da8dSAndroid Build Coastguard Worker        if member.gname is not None:
799*cda5da8dSAndroid Build Coastguard Worker            new_attrs['gname'] = None
800*cda5da8dSAndroid Build Coastguard Worker        # Check link destination for 'data'
801*cda5da8dSAndroid Build Coastguard Worker        if member.islnk() or member.issym():
802*cda5da8dSAndroid Build Coastguard Worker            if os.path.isabs(member.linkname):
803*cda5da8dSAndroid Build Coastguard Worker                raise AbsoluteLinkError(member)
804*cda5da8dSAndroid Build Coastguard Worker            target_path = os.path.realpath(os.path.join(dest_path, member.linkname))
805*cda5da8dSAndroid Build Coastguard Worker            if os.path.commonpath([target_path, dest_path]) != dest_path:
806*cda5da8dSAndroid Build Coastguard Worker                raise LinkOutsideDestinationError(member, target_path)
807*cda5da8dSAndroid Build Coastguard Worker    return new_attrs
808*cda5da8dSAndroid Build Coastguard Worker
809*cda5da8dSAndroid Build Coastguard Workerdef fully_trusted_filter(member, dest_path):
810*cda5da8dSAndroid Build Coastguard Worker    return member
811*cda5da8dSAndroid Build Coastguard Worker
812*cda5da8dSAndroid Build Coastguard Workerdef tar_filter(member, dest_path):
813*cda5da8dSAndroid Build Coastguard Worker    new_attrs = _get_filtered_attrs(member, dest_path, False)
814*cda5da8dSAndroid Build Coastguard Worker    if new_attrs:
815*cda5da8dSAndroid Build Coastguard Worker        return member.replace(**new_attrs, deep=False)
816*cda5da8dSAndroid Build Coastguard Worker    return member
817*cda5da8dSAndroid Build Coastguard Worker
818*cda5da8dSAndroid Build Coastguard Workerdef data_filter(member, dest_path):
819*cda5da8dSAndroid Build Coastguard Worker    new_attrs = _get_filtered_attrs(member, dest_path, True)
820*cda5da8dSAndroid Build Coastguard Worker    if new_attrs:
821*cda5da8dSAndroid Build Coastguard Worker        return member.replace(**new_attrs, deep=False)
822*cda5da8dSAndroid Build Coastguard Worker    return member
823*cda5da8dSAndroid Build Coastguard Worker
824*cda5da8dSAndroid Build Coastguard Worker_NAMED_FILTERS = {
825*cda5da8dSAndroid Build Coastguard Worker    "fully_trusted": fully_trusted_filter,
826*cda5da8dSAndroid Build Coastguard Worker    "tar": tar_filter,
827*cda5da8dSAndroid Build Coastguard Worker    "data": data_filter,
828*cda5da8dSAndroid Build Coastguard Worker}
829*cda5da8dSAndroid Build Coastguard Worker
830*cda5da8dSAndroid Build Coastguard Worker#------------------
831*cda5da8dSAndroid Build Coastguard Worker# Exported Classes
832*cda5da8dSAndroid Build Coastguard Worker#------------------
833*cda5da8dSAndroid Build Coastguard Worker
834*cda5da8dSAndroid Build Coastguard Worker# Sentinel for replace() defaults, meaning "don't change the attribute"
835*cda5da8dSAndroid Build Coastguard Worker_KEEP = object()
836*cda5da8dSAndroid Build Coastguard Worker
837*cda5da8dSAndroid Build Coastguard Workerclass TarInfo(object):
838*cda5da8dSAndroid Build Coastguard Worker    """Informational class which holds the details about an
839*cda5da8dSAndroid Build Coastguard Worker       archive member given by a tar header block.
840*cda5da8dSAndroid Build Coastguard Worker       TarInfo objects are returned by TarFile.getmember(),
841*cda5da8dSAndroid Build Coastguard Worker       TarFile.getmembers() and TarFile.gettarinfo() and are
842*cda5da8dSAndroid Build Coastguard Worker       usually created internally.
843*cda5da8dSAndroid Build Coastguard Worker    """
844*cda5da8dSAndroid Build Coastguard Worker
845*cda5da8dSAndroid Build Coastguard Worker    __slots__ = dict(
846*cda5da8dSAndroid Build Coastguard Worker        name = 'Name of the archive member.',
847*cda5da8dSAndroid Build Coastguard Worker        mode = 'Permission bits.',
848*cda5da8dSAndroid Build Coastguard Worker        uid = 'User ID of the user who originally stored this member.',
849*cda5da8dSAndroid Build Coastguard Worker        gid = 'Group ID of the user who originally stored this member.',
850*cda5da8dSAndroid Build Coastguard Worker        size = 'Size in bytes.',
851*cda5da8dSAndroid Build Coastguard Worker        mtime = 'Time of last modification.',
852*cda5da8dSAndroid Build Coastguard Worker        chksum = 'Header checksum.',
853*cda5da8dSAndroid Build Coastguard Worker        type = ('File type. type is usually one of these constants: '
854*cda5da8dSAndroid Build Coastguard Worker                'REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, '
855*cda5da8dSAndroid Build Coastguard Worker                'CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE.'),
856*cda5da8dSAndroid Build Coastguard Worker        linkname = ('Name of the target file name, which is only present '
857*cda5da8dSAndroid Build Coastguard Worker                    'in TarInfo objects of type LNKTYPE and SYMTYPE.'),
858*cda5da8dSAndroid Build Coastguard Worker        uname = 'User name.',
859*cda5da8dSAndroid Build Coastguard Worker        gname = 'Group name.',
860*cda5da8dSAndroid Build Coastguard Worker        devmajor = 'Device major number.',
861*cda5da8dSAndroid Build Coastguard Worker        devminor = 'Device minor number.',
862*cda5da8dSAndroid Build Coastguard Worker        offset = 'The tar header starts here.',
863*cda5da8dSAndroid Build Coastguard Worker        offset_data = "The file's data starts here.",
864*cda5da8dSAndroid Build Coastguard Worker        pax_headers = ('A dictionary containing key-value pairs of an '
865*cda5da8dSAndroid Build Coastguard Worker                       'associated pax extended header.'),
866*cda5da8dSAndroid Build Coastguard Worker        sparse = 'Sparse member information.',
867*cda5da8dSAndroid Build Coastguard Worker        tarfile = None,
868*cda5da8dSAndroid Build Coastguard Worker        _sparse_structs = None,
869*cda5da8dSAndroid Build Coastguard Worker        _link_target = None,
870*cda5da8dSAndroid Build Coastguard Worker        )
871*cda5da8dSAndroid Build Coastguard Worker
872*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, name=""):
873*cda5da8dSAndroid Build Coastguard Worker        """Construct a TarInfo object. name is the optional name
874*cda5da8dSAndroid Build Coastguard Worker           of the member.
875*cda5da8dSAndroid Build Coastguard Worker        """
876*cda5da8dSAndroid Build Coastguard Worker        self.name = name        # member name
877*cda5da8dSAndroid Build Coastguard Worker        self.mode = 0o644       # file permissions
878*cda5da8dSAndroid Build Coastguard Worker        self.uid = 0            # user id
879*cda5da8dSAndroid Build Coastguard Worker        self.gid = 0            # group id
880*cda5da8dSAndroid Build Coastguard Worker        self.size = 0           # file size
881*cda5da8dSAndroid Build Coastguard Worker        self.mtime = 0          # modification time
882*cda5da8dSAndroid Build Coastguard Worker        self.chksum = 0         # header checksum
883*cda5da8dSAndroid Build Coastguard Worker        self.type = REGTYPE     # member type
884*cda5da8dSAndroid Build Coastguard Worker        self.linkname = ""      # link name
885*cda5da8dSAndroid Build Coastguard Worker        self.uname = ""         # user name
886*cda5da8dSAndroid Build Coastguard Worker        self.gname = ""         # group name
887*cda5da8dSAndroid Build Coastguard Worker        self.devmajor = 0       # device major number
888*cda5da8dSAndroid Build Coastguard Worker        self.devminor = 0       # device minor number
889*cda5da8dSAndroid Build Coastguard Worker
890*cda5da8dSAndroid Build Coastguard Worker        self.offset = 0         # the tar header starts here
891*cda5da8dSAndroid Build Coastguard Worker        self.offset_data = 0    # the file's data starts here
892*cda5da8dSAndroid Build Coastguard Worker
893*cda5da8dSAndroid Build Coastguard Worker        self.sparse = None      # sparse member information
894*cda5da8dSAndroid Build Coastguard Worker        self.pax_headers = {}   # pax header information
895*cda5da8dSAndroid Build Coastguard Worker
896*cda5da8dSAndroid Build Coastguard Worker    @property
897*cda5da8dSAndroid Build Coastguard Worker    def path(self):
898*cda5da8dSAndroid Build Coastguard Worker        'In pax headers, "name" is called "path".'
899*cda5da8dSAndroid Build Coastguard Worker        return self.name
900*cda5da8dSAndroid Build Coastguard Worker
901*cda5da8dSAndroid Build Coastguard Worker    @path.setter
902*cda5da8dSAndroid Build Coastguard Worker    def path(self, name):
903*cda5da8dSAndroid Build Coastguard Worker        self.name = name
904*cda5da8dSAndroid Build Coastguard Worker
905*cda5da8dSAndroid Build Coastguard Worker    @property
906*cda5da8dSAndroid Build Coastguard Worker    def linkpath(self):
907*cda5da8dSAndroid Build Coastguard Worker        'In pax headers, "linkname" is called "linkpath".'
908*cda5da8dSAndroid Build Coastguard Worker        return self.linkname
909*cda5da8dSAndroid Build Coastguard Worker
910*cda5da8dSAndroid Build Coastguard Worker    @linkpath.setter
911*cda5da8dSAndroid Build Coastguard Worker    def linkpath(self, linkname):
912*cda5da8dSAndroid Build Coastguard Worker        self.linkname = linkname
913*cda5da8dSAndroid Build Coastguard Worker
914*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
915*cda5da8dSAndroid Build Coastguard Worker        return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self))
916*cda5da8dSAndroid Build Coastguard Worker
917*cda5da8dSAndroid Build Coastguard Worker    def replace(self, *,
918*cda5da8dSAndroid Build Coastguard Worker                name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP,
919*cda5da8dSAndroid Build Coastguard Worker                uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP,
920*cda5da8dSAndroid Build Coastguard Worker                deep=True, _KEEP=_KEEP):
921*cda5da8dSAndroid Build Coastguard Worker        """Return a deep copy of self with the given attributes replaced.
922*cda5da8dSAndroid Build Coastguard Worker        """
923*cda5da8dSAndroid Build Coastguard Worker        if deep:
924*cda5da8dSAndroid Build Coastguard Worker            result = copy.deepcopy(self)
925*cda5da8dSAndroid Build Coastguard Worker        else:
926*cda5da8dSAndroid Build Coastguard Worker            result = copy.copy(self)
927*cda5da8dSAndroid Build Coastguard Worker        if name is not _KEEP:
928*cda5da8dSAndroid Build Coastguard Worker            result.name = name
929*cda5da8dSAndroid Build Coastguard Worker        if mtime is not _KEEP:
930*cda5da8dSAndroid Build Coastguard Worker            result.mtime = mtime
931*cda5da8dSAndroid Build Coastguard Worker        if mode is not _KEEP:
932*cda5da8dSAndroid Build Coastguard Worker            result.mode = mode
933*cda5da8dSAndroid Build Coastguard Worker        if linkname is not _KEEP:
934*cda5da8dSAndroid Build Coastguard Worker            result.linkname = linkname
935*cda5da8dSAndroid Build Coastguard Worker        if uid is not _KEEP:
936*cda5da8dSAndroid Build Coastguard Worker            result.uid = uid
937*cda5da8dSAndroid Build Coastguard Worker        if gid is not _KEEP:
938*cda5da8dSAndroid Build Coastguard Worker            result.gid = gid
939*cda5da8dSAndroid Build Coastguard Worker        if uname is not _KEEP:
940*cda5da8dSAndroid Build Coastguard Worker            result.uname = uname
941*cda5da8dSAndroid Build Coastguard Worker        if gname is not _KEEP:
942*cda5da8dSAndroid Build Coastguard Worker            result.gname = gname
943*cda5da8dSAndroid Build Coastguard Worker        return result
944*cda5da8dSAndroid Build Coastguard Worker
945*cda5da8dSAndroid Build Coastguard Worker    def get_info(self):
946*cda5da8dSAndroid Build Coastguard Worker        """Return the TarInfo's attributes as a dictionary.
947*cda5da8dSAndroid Build Coastguard Worker        """
948*cda5da8dSAndroid Build Coastguard Worker        if self.mode is None:
949*cda5da8dSAndroid Build Coastguard Worker            mode = None
950*cda5da8dSAndroid Build Coastguard Worker        else:
951*cda5da8dSAndroid Build Coastguard Worker            mode = self.mode & 0o7777
952*cda5da8dSAndroid Build Coastguard Worker        info = {
953*cda5da8dSAndroid Build Coastguard Worker            "name":     self.name,
954*cda5da8dSAndroid Build Coastguard Worker            "mode":     mode,
955*cda5da8dSAndroid Build Coastguard Worker            "uid":      self.uid,
956*cda5da8dSAndroid Build Coastguard Worker            "gid":      self.gid,
957*cda5da8dSAndroid Build Coastguard Worker            "size":     self.size,
958*cda5da8dSAndroid Build Coastguard Worker            "mtime":    self.mtime,
959*cda5da8dSAndroid Build Coastguard Worker            "chksum":   self.chksum,
960*cda5da8dSAndroid Build Coastguard Worker            "type":     self.type,
961*cda5da8dSAndroid Build Coastguard Worker            "linkname": self.linkname,
962*cda5da8dSAndroid Build Coastguard Worker            "uname":    self.uname,
963*cda5da8dSAndroid Build Coastguard Worker            "gname":    self.gname,
964*cda5da8dSAndroid Build Coastguard Worker            "devmajor": self.devmajor,
965*cda5da8dSAndroid Build Coastguard Worker            "devminor": self.devminor
966*cda5da8dSAndroid Build Coastguard Worker        }
967*cda5da8dSAndroid Build Coastguard Worker
968*cda5da8dSAndroid Build Coastguard Worker        if info["type"] == DIRTYPE and not info["name"].endswith("/"):
969*cda5da8dSAndroid Build Coastguard Worker            info["name"] += "/"
970*cda5da8dSAndroid Build Coastguard Worker
971*cda5da8dSAndroid Build Coastguard Worker        return info
972*cda5da8dSAndroid Build Coastguard Worker
973*cda5da8dSAndroid Build Coastguard Worker    def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"):
974*cda5da8dSAndroid Build Coastguard Worker        """Return a tar header as a string of 512 byte blocks.
975*cda5da8dSAndroid Build Coastguard Worker        """
976*cda5da8dSAndroid Build Coastguard Worker        info = self.get_info()
977*cda5da8dSAndroid Build Coastguard Worker        for name, value in info.items():
978*cda5da8dSAndroid Build Coastguard Worker            if value is None:
979*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("%s may not be None" % name)
980*cda5da8dSAndroid Build Coastguard Worker
981*cda5da8dSAndroid Build Coastguard Worker        if format == USTAR_FORMAT:
982*cda5da8dSAndroid Build Coastguard Worker            return self.create_ustar_header(info, encoding, errors)
983*cda5da8dSAndroid Build Coastguard Worker        elif format == GNU_FORMAT:
984*cda5da8dSAndroid Build Coastguard Worker            return self.create_gnu_header(info, encoding, errors)
985*cda5da8dSAndroid Build Coastguard Worker        elif format == PAX_FORMAT:
986*cda5da8dSAndroid Build Coastguard Worker            return self.create_pax_header(info, encoding)
987*cda5da8dSAndroid Build Coastguard Worker        else:
988*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("invalid format")
989*cda5da8dSAndroid Build Coastguard Worker
990*cda5da8dSAndroid Build Coastguard Worker    def create_ustar_header(self, info, encoding, errors):
991*cda5da8dSAndroid Build Coastguard Worker        """Return the object as a ustar header block.
992*cda5da8dSAndroid Build Coastguard Worker        """
993*cda5da8dSAndroid Build Coastguard Worker        info["magic"] = POSIX_MAGIC
994*cda5da8dSAndroid Build Coastguard Worker
995*cda5da8dSAndroid Build Coastguard Worker        if len(info["linkname"].encode(encoding, errors)) > LENGTH_LINK:
996*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("linkname is too long")
997*cda5da8dSAndroid Build Coastguard Worker
998*cda5da8dSAndroid Build Coastguard Worker        if len(info["name"].encode(encoding, errors)) > LENGTH_NAME:
999*cda5da8dSAndroid Build Coastguard Worker            info["prefix"], info["name"] = self._posix_split_name(info["name"], encoding, errors)
1000*cda5da8dSAndroid Build Coastguard Worker
1001*cda5da8dSAndroid Build Coastguard Worker        return self._create_header(info, USTAR_FORMAT, encoding, errors)
1002*cda5da8dSAndroid Build Coastguard Worker
1003*cda5da8dSAndroid Build Coastguard Worker    def create_gnu_header(self, info, encoding, errors):
1004*cda5da8dSAndroid Build Coastguard Worker        """Return the object as a GNU header block sequence.
1005*cda5da8dSAndroid Build Coastguard Worker        """
1006*cda5da8dSAndroid Build Coastguard Worker        info["magic"] = GNU_MAGIC
1007*cda5da8dSAndroid Build Coastguard Worker
1008*cda5da8dSAndroid Build Coastguard Worker        buf = b""
1009*cda5da8dSAndroid Build Coastguard Worker        if len(info["linkname"].encode(encoding, errors)) > LENGTH_LINK:
1010*cda5da8dSAndroid Build Coastguard Worker            buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors)
1011*cda5da8dSAndroid Build Coastguard Worker
1012*cda5da8dSAndroid Build Coastguard Worker        if len(info["name"].encode(encoding, errors)) > LENGTH_NAME:
1013*cda5da8dSAndroid Build Coastguard Worker            buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors)
1014*cda5da8dSAndroid Build Coastguard Worker
1015*cda5da8dSAndroid Build Coastguard Worker        return buf + self._create_header(info, GNU_FORMAT, encoding, errors)
1016*cda5da8dSAndroid Build Coastguard Worker
1017*cda5da8dSAndroid Build Coastguard Worker    def create_pax_header(self, info, encoding):
1018*cda5da8dSAndroid Build Coastguard Worker        """Return the object as a ustar header block. If it cannot be
1019*cda5da8dSAndroid Build Coastguard Worker           represented this way, prepend a pax extended header sequence
1020*cda5da8dSAndroid Build Coastguard Worker           with supplement information.
1021*cda5da8dSAndroid Build Coastguard Worker        """
1022*cda5da8dSAndroid Build Coastguard Worker        info["magic"] = POSIX_MAGIC
1023*cda5da8dSAndroid Build Coastguard Worker        pax_headers = self.pax_headers.copy()
1024*cda5da8dSAndroid Build Coastguard Worker
1025*cda5da8dSAndroid Build Coastguard Worker        # Test string fields for values that exceed the field length or cannot
1026*cda5da8dSAndroid Build Coastguard Worker        # be represented in ASCII encoding.
1027*cda5da8dSAndroid Build Coastguard Worker        for name, hname, length in (
1028*cda5da8dSAndroid Build Coastguard Worker                ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK),
1029*cda5da8dSAndroid Build Coastguard Worker                ("uname", "uname", 32), ("gname", "gname", 32)):
1030*cda5da8dSAndroid Build Coastguard Worker
1031*cda5da8dSAndroid Build Coastguard Worker            if hname in pax_headers:
1032*cda5da8dSAndroid Build Coastguard Worker                # The pax header has priority.
1033*cda5da8dSAndroid Build Coastguard Worker                continue
1034*cda5da8dSAndroid Build Coastguard Worker
1035*cda5da8dSAndroid Build Coastguard Worker            # Try to encode the string as ASCII.
1036*cda5da8dSAndroid Build Coastguard Worker            try:
1037*cda5da8dSAndroid Build Coastguard Worker                info[name].encode("ascii", "strict")
1038*cda5da8dSAndroid Build Coastguard Worker            except UnicodeEncodeError:
1039*cda5da8dSAndroid Build Coastguard Worker                pax_headers[hname] = info[name]
1040*cda5da8dSAndroid Build Coastguard Worker                continue
1041*cda5da8dSAndroid Build Coastguard Worker
1042*cda5da8dSAndroid Build Coastguard Worker            if len(info[name]) > length:
1043*cda5da8dSAndroid Build Coastguard Worker                pax_headers[hname] = info[name]
1044*cda5da8dSAndroid Build Coastguard Worker
1045*cda5da8dSAndroid Build Coastguard Worker        # Test number fields for values that exceed the field limit or values
1046*cda5da8dSAndroid Build Coastguard Worker        # that like to be stored as float.
1047*cda5da8dSAndroid Build Coastguard Worker        for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)):
1048*cda5da8dSAndroid Build Coastguard Worker            needs_pax = False
1049*cda5da8dSAndroid Build Coastguard Worker
1050*cda5da8dSAndroid Build Coastguard Worker            val = info[name]
1051*cda5da8dSAndroid Build Coastguard Worker            val_is_float = isinstance(val, float)
1052*cda5da8dSAndroid Build Coastguard Worker            val_int = round(val) if val_is_float else val
1053*cda5da8dSAndroid Build Coastguard Worker            if not 0 <= val_int < 8 ** (digits - 1):
1054*cda5da8dSAndroid Build Coastguard Worker                # Avoid overflow.
1055*cda5da8dSAndroid Build Coastguard Worker                info[name] = 0
1056*cda5da8dSAndroid Build Coastguard Worker                needs_pax = True
1057*cda5da8dSAndroid Build Coastguard Worker            elif val_is_float:
1058*cda5da8dSAndroid Build Coastguard Worker                # Put rounded value in ustar header, and full
1059*cda5da8dSAndroid Build Coastguard Worker                # precision value in pax header.
1060*cda5da8dSAndroid Build Coastguard Worker                info[name] = val_int
1061*cda5da8dSAndroid Build Coastguard Worker                needs_pax = True
1062*cda5da8dSAndroid Build Coastguard Worker
1063*cda5da8dSAndroid Build Coastguard Worker            # The existing pax header has priority.
1064*cda5da8dSAndroid Build Coastguard Worker            if needs_pax and name not in pax_headers:
1065*cda5da8dSAndroid Build Coastguard Worker                pax_headers[name] = str(val)
1066*cda5da8dSAndroid Build Coastguard Worker
1067*cda5da8dSAndroid Build Coastguard Worker        # Create a pax extended header if necessary.
1068*cda5da8dSAndroid Build Coastguard Worker        if pax_headers:
1069*cda5da8dSAndroid Build Coastguard Worker            buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding)
1070*cda5da8dSAndroid Build Coastguard Worker        else:
1071*cda5da8dSAndroid Build Coastguard Worker            buf = b""
1072*cda5da8dSAndroid Build Coastguard Worker
1073*cda5da8dSAndroid Build Coastguard Worker        return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace")
1074*cda5da8dSAndroid Build Coastguard Worker
1075*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1076*cda5da8dSAndroid Build Coastguard Worker    def create_pax_global_header(cls, pax_headers):
1077*cda5da8dSAndroid Build Coastguard Worker        """Return the object as a pax global header block sequence.
1078*cda5da8dSAndroid Build Coastguard Worker        """
1079*cda5da8dSAndroid Build Coastguard Worker        return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf-8")
1080*cda5da8dSAndroid Build Coastguard Worker
1081*cda5da8dSAndroid Build Coastguard Worker    def _posix_split_name(self, name, encoding, errors):
1082*cda5da8dSAndroid Build Coastguard Worker        """Split a name longer than 100 chars into a prefix
1083*cda5da8dSAndroid Build Coastguard Worker           and a name part.
1084*cda5da8dSAndroid Build Coastguard Worker        """
1085*cda5da8dSAndroid Build Coastguard Worker        components = name.split("/")
1086*cda5da8dSAndroid Build Coastguard Worker        for i in range(1, len(components)):
1087*cda5da8dSAndroid Build Coastguard Worker            prefix = "/".join(components[:i])
1088*cda5da8dSAndroid Build Coastguard Worker            name = "/".join(components[i:])
1089*cda5da8dSAndroid Build Coastguard Worker            if len(prefix.encode(encoding, errors)) <= LENGTH_PREFIX and \
1090*cda5da8dSAndroid Build Coastguard Worker                    len(name.encode(encoding, errors)) <= LENGTH_NAME:
1091*cda5da8dSAndroid Build Coastguard Worker                break
1092*cda5da8dSAndroid Build Coastguard Worker        else:
1093*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("name is too long")
1094*cda5da8dSAndroid Build Coastguard Worker
1095*cda5da8dSAndroid Build Coastguard Worker        return prefix, name
1096*cda5da8dSAndroid Build Coastguard Worker
1097*cda5da8dSAndroid Build Coastguard Worker    @staticmethod
1098*cda5da8dSAndroid Build Coastguard Worker    def _create_header(info, format, encoding, errors):
1099*cda5da8dSAndroid Build Coastguard Worker        """Return a header block. info is a dictionary with file
1100*cda5da8dSAndroid Build Coastguard Worker           information, format must be one of the *_FORMAT constants.
1101*cda5da8dSAndroid Build Coastguard Worker        """
1102*cda5da8dSAndroid Build Coastguard Worker        has_device_fields = info.get("type") in (CHRTYPE, BLKTYPE)
1103*cda5da8dSAndroid Build Coastguard Worker        if has_device_fields:
1104*cda5da8dSAndroid Build Coastguard Worker            devmajor = itn(info.get("devmajor", 0), 8, format)
1105*cda5da8dSAndroid Build Coastguard Worker            devminor = itn(info.get("devminor", 0), 8, format)
1106*cda5da8dSAndroid Build Coastguard Worker        else:
1107*cda5da8dSAndroid Build Coastguard Worker            devmajor = stn("", 8, encoding, errors)
1108*cda5da8dSAndroid Build Coastguard Worker            devminor = stn("", 8, encoding, errors)
1109*cda5da8dSAndroid Build Coastguard Worker
1110*cda5da8dSAndroid Build Coastguard Worker        # None values in metadata should cause ValueError.
1111*cda5da8dSAndroid Build Coastguard Worker        # itn()/stn() do this for all fields except type.
1112*cda5da8dSAndroid Build Coastguard Worker        filetype = info.get("type", REGTYPE)
1113*cda5da8dSAndroid Build Coastguard Worker        if filetype is None:
1114*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("TarInfo.type must not be None")
1115*cda5da8dSAndroid Build Coastguard Worker
1116*cda5da8dSAndroid Build Coastguard Worker        parts = [
1117*cda5da8dSAndroid Build Coastguard Worker            stn(info.get("name", ""), 100, encoding, errors),
1118*cda5da8dSAndroid Build Coastguard Worker            itn(info.get("mode", 0) & 0o7777, 8, format),
1119*cda5da8dSAndroid Build Coastguard Worker            itn(info.get("uid", 0), 8, format),
1120*cda5da8dSAndroid Build Coastguard Worker            itn(info.get("gid", 0), 8, format),
1121*cda5da8dSAndroid Build Coastguard Worker            itn(info.get("size", 0), 12, format),
1122*cda5da8dSAndroid Build Coastguard Worker            itn(info.get("mtime", 0), 12, format),
1123*cda5da8dSAndroid Build Coastguard Worker            b"        ", # checksum field
1124*cda5da8dSAndroid Build Coastguard Worker            filetype,
1125*cda5da8dSAndroid Build Coastguard Worker            stn(info.get("linkname", ""), 100, encoding, errors),
1126*cda5da8dSAndroid Build Coastguard Worker            info.get("magic", POSIX_MAGIC),
1127*cda5da8dSAndroid Build Coastguard Worker            stn(info.get("uname", ""), 32, encoding, errors),
1128*cda5da8dSAndroid Build Coastguard Worker            stn(info.get("gname", ""), 32, encoding, errors),
1129*cda5da8dSAndroid Build Coastguard Worker            devmajor,
1130*cda5da8dSAndroid Build Coastguard Worker            devminor,
1131*cda5da8dSAndroid Build Coastguard Worker            stn(info.get("prefix", ""), 155, encoding, errors)
1132*cda5da8dSAndroid Build Coastguard Worker        ]
1133*cda5da8dSAndroid Build Coastguard Worker
1134*cda5da8dSAndroid Build Coastguard Worker        buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts))
1135*cda5da8dSAndroid Build Coastguard Worker        chksum = calc_chksums(buf[-BLOCKSIZE:])[0]
1136*cda5da8dSAndroid Build Coastguard Worker        buf = buf[:-364] + bytes("%06o\0" % chksum, "ascii") + buf[-357:]
1137*cda5da8dSAndroid Build Coastguard Worker        return buf
1138*cda5da8dSAndroid Build Coastguard Worker
1139*cda5da8dSAndroid Build Coastguard Worker    @staticmethod
1140*cda5da8dSAndroid Build Coastguard Worker    def _create_payload(payload):
1141*cda5da8dSAndroid Build Coastguard Worker        """Return the string payload filled with zero bytes
1142*cda5da8dSAndroid Build Coastguard Worker           up to the next 512 byte border.
1143*cda5da8dSAndroid Build Coastguard Worker        """
1144*cda5da8dSAndroid Build Coastguard Worker        blocks, remainder = divmod(len(payload), BLOCKSIZE)
1145*cda5da8dSAndroid Build Coastguard Worker        if remainder > 0:
1146*cda5da8dSAndroid Build Coastguard Worker            payload += (BLOCKSIZE - remainder) * NUL
1147*cda5da8dSAndroid Build Coastguard Worker        return payload
1148*cda5da8dSAndroid Build Coastguard Worker
1149*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1150*cda5da8dSAndroid Build Coastguard Worker    def _create_gnu_long_header(cls, name, type, encoding, errors):
1151*cda5da8dSAndroid Build Coastguard Worker        """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence
1152*cda5da8dSAndroid Build Coastguard Worker           for name.
1153*cda5da8dSAndroid Build Coastguard Worker        """
1154*cda5da8dSAndroid Build Coastguard Worker        name = name.encode(encoding, errors) + NUL
1155*cda5da8dSAndroid Build Coastguard Worker
1156*cda5da8dSAndroid Build Coastguard Worker        info = {}
1157*cda5da8dSAndroid Build Coastguard Worker        info["name"] = "././@LongLink"
1158*cda5da8dSAndroid Build Coastguard Worker        info["type"] = type
1159*cda5da8dSAndroid Build Coastguard Worker        info["size"] = len(name)
1160*cda5da8dSAndroid Build Coastguard Worker        info["magic"] = GNU_MAGIC
1161*cda5da8dSAndroid Build Coastguard Worker
1162*cda5da8dSAndroid Build Coastguard Worker        # create extended header + name blocks.
1163*cda5da8dSAndroid Build Coastguard Worker        return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \
1164*cda5da8dSAndroid Build Coastguard Worker                cls._create_payload(name)
1165*cda5da8dSAndroid Build Coastguard Worker
1166*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1167*cda5da8dSAndroid Build Coastguard Worker    def _create_pax_generic_header(cls, pax_headers, type, encoding):
1168*cda5da8dSAndroid Build Coastguard Worker        """Return a POSIX.1-2008 extended or global header sequence
1169*cda5da8dSAndroid Build Coastguard Worker           that contains a list of keyword, value pairs. The values
1170*cda5da8dSAndroid Build Coastguard Worker           must be strings.
1171*cda5da8dSAndroid Build Coastguard Worker        """
1172*cda5da8dSAndroid Build Coastguard Worker        # Check if one of the fields contains surrogate characters and thereby
1173*cda5da8dSAndroid Build Coastguard Worker        # forces hdrcharset=BINARY, see _proc_pax() for more information.
1174*cda5da8dSAndroid Build Coastguard Worker        binary = False
1175*cda5da8dSAndroid Build Coastguard Worker        for keyword, value in pax_headers.items():
1176*cda5da8dSAndroid Build Coastguard Worker            try:
1177*cda5da8dSAndroid Build Coastguard Worker                value.encode("utf-8", "strict")
1178*cda5da8dSAndroid Build Coastguard Worker            except UnicodeEncodeError:
1179*cda5da8dSAndroid Build Coastguard Worker                binary = True
1180*cda5da8dSAndroid Build Coastguard Worker                break
1181*cda5da8dSAndroid Build Coastguard Worker
1182*cda5da8dSAndroid Build Coastguard Worker        records = b""
1183*cda5da8dSAndroid Build Coastguard Worker        if binary:
1184*cda5da8dSAndroid Build Coastguard Worker            # Put the hdrcharset field at the beginning of the header.
1185*cda5da8dSAndroid Build Coastguard Worker            records += b"21 hdrcharset=BINARY\n"
1186*cda5da8dSAndroid Build Coastguard Worker
1187*cda5da8dSAndroid Build Coastguard Worker        for keyword, value in pax_headers.items():
1188*cda5da8dSAndroid Build Coastguard Worker            keyword = keyword.encode("utf-8")
1189*cda5da8dSAndroid Build Coastguard Worker            if binary:
1190*cda5da8dSAndroid Build Coastguard Worker                # Try to restore the original byte representation of `value'.
1191*cda5da8dSAndroid Build Coastguard Worker                # Needless to say, that the encoding must match the string.
1192*cda5da8dSAndroid Build Coastguard Worker                value = value.encode(encoding, "surrogateescape")
1193*cda5da8dSAndroid Build Coastguard Worker            else:
1194*cda5da8dSAndroid Build Coastguard Worker                value = value.encode("utf-8")
1195*cda5da8dSAndroid Build Coastguard Worker
1196*cda5da8dSAndroid Build Coastguard Worker            l = len(keyword) + len(value) + 3   # ' ' + '=' + '\n'
1197*cda5da8dSAndroid Build Coastguard Worker            n = p = 0
1198*cda5da8dSAndroid Build Coastguard Worker            while True:
1199*cda5da8dSAndroid Build Coastguard Worker                n = l + len(str(p))
1200*cda5da8dSAndroid Build Coastguard Worker                if n == p:
1201*cda5da8dSAndroid Build Coastguard Worker                    break
1202*cda5da8dSAndroid Build Coastguard Worker                p = n
1203*cda5da8dSAndroid Build Coastguard Worker            records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n"
1204*cda5da8dSAndroid Build Coastguard Worker
1205*cda5da8dSAndroid Build Coastguard Worker        # We use a hardcoded "././@PaxHeader" name like star does
1206*cda5da8dSAndroid Build Coastguard Worker        # instead of the one that POSIX recommends.
1207*cda5da8dSAndroid Build Coastguard Worker        info = {}
1208*cda5da8dSAndroid Build Coastguard Worker        info["name"] = "././@PaxHeader"
1209*cda5da8dSAndroid Build Coastguard Worker        info["type"] = type
1210*cda5da8dSAndroid Build Coastguard Worker        info["size"] = len(records)
1211*cda5da8dSAndroid Build Coastguard Worker        info["magic"] = POSIX_MAGIC
1212*cda5da8dSAndroid Build Coastguard Worker
1213*cda5da8dSAndroid Build Coastguard Worker        # Create pax header + record blocks.
1214*cda5da8dSAndroid Build Coastguard Worker        return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \
1215*cda5da8dSAndroid Build Coastguard Worker                cls._create_payload(records)
1216*cda5da8dSAndroid Build Coastguard Worker
1217*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1218*cda5da8dSAndroid Build Coastguard Worker    def frombuf(cls, buf, encoding, errors):
1219*cda5da8dSAndroid Build Coastguard Worker        """Construct a TarInfo object from a 512 byte bytes object.
1220*cda5da8dSAndroid Build Coastguard Worker        """
1221*cda5da8dSAndroid Build Coastguard Worker        if len(buf) == 0:
1222*cda5da8dSAndroid Build Coastguard Worker            raise EmptyHeaderError("empty header")
1223*cda5da8dSAndroid Build Coastguard Worker        if len(buf) != BLOCKSIZE:
1224*cda5da8dSAndroid Build Coastguard Worker            raise TruncatedHeaderError("truncated header")
1225*cda5da8dSAndroid Build Coastguard Worker        if buf.count(NUL) == BLOCKSIZE:
1226*cda5da8dSAndroid Build Coastguard Worker            raise EOFHeaderError("end of file header")
1227*cda5da8dSAndroid Build Coastguard Worker
1228*cda5da8dSAndroid Build Coastguard Worker        chksum = nti(buf[148:156])
1229*cda5da8dSAndroid Build Coastguard Worker        if chksum not in calc_chksums(buf):
1230*cda5da8dSAndroid Build Coastguard Worker            raise InvalidHeaderError("bad checksum")
1231*cda5da8dSAndroid Build Coastguard Worker
1232*cda5da8dSAndroid Build Coastguard Worker        obj = cls()
1233*cda5da8dSAndroid Build Coastguard Worker        obj.name = nts(buf[0:100], encoding, errors)
1234*cda5da8dSAndroid Build Coastguard Worker        obj.mode = nti(buf[100:108])
1235*cda5da8dSAndroid Build Coastguard Worker        obj.uid = nti(buf[108:116])
1236*cda5da8dSAndroid Build Coastguard Worker        obj.gid = nti(buf[116:124])
1237*cda5da8dSAndroid Build Coastguard Worker        obj.size = nti(buf[124:136])
1238*cda5da8dSAndroid Build Coastguard Worker        obj.mtime = nti(buf[136:148])
1239*cda5da8dSAndroid Build Coastguard Worker        obj.chksum = chksum
1240*cda5da8dSAndroid Build Coastguard Worker        obj.type = buf[156:157]
1241*cda5da8dSAndroid Build Coastguard Worker        obj.linkname = nts(buf[157:257], encoding, errors)
1242*cda5da8dSAndroid Build Coastguard Worker        obj.uname = nts(buf[265:297], encoding, errors)
1243*cda5da8dSAndroid Build Coastguard Worker        obj.gname = nts(buf[297:329], encoding, errors)
1244*cda5da8dSAndroid Build Coastguard Worker        obj.devmajor = nti(buf[329:337])
1245*cda5da8dSAndroid Build Coastguard Worker        obj.devminor = nti(buf[337:345])
1246*cda5da8dSAndroid Build Coastguard Worker        prefix = nts(buf[345:500], encoding, errors)
1247*cda5da8dSAndroid Build Coastguard Worker
1248*cda5da8dSAndroid Build Coastguard Worker        # Old V7 tar format represents a directory as a regular
1249*cda5da8dSAndroid Build Coastguard Worker        # file with a trailing slash.
1250*cda5da8dSAndroid Build Coastguard Worker        if obj.type == AREGTYPE and obj.name.endswith("/"):
1251*cda5da8dSAndroid Build Coastguard Worker            obj.type = DIRTYPE
1252*cda5da8dSAndroid Build Coastguard Worker
1253*cda5da8dSAndroid Build Coastguard Worker        # The old GNU sparse format occupies some of the unused
1254*cda5da8dSAndroid Build Coastguard Worker        # space in the buffer for up to 4 sparse structures.
1255*cda5da8dSAndroid Build Coastguard Worker        # Save them for later processing in _proc_sparse().
1256*cda5da8dSAndroid Build Coastguard Worker        if obj.type == GNUTYPE_SPARSE:
1257*cda5da8dSAndroid Build Coastguard Worker            pos = 386
1258*cda5da8dSAndroid Build Coastguard Worker            structs = []
1259*cda5da8dSAndroid Build Coastguard Worker            for i in range(4):
1260*cda5da8dSAndroid Build Coastguard Worker                try:
1261*cda5da8dSAndroid Build Coastguard Worker                    offset = nti(buf[pos:pos + 12])
1262*cda5da8dSAndroid Build Coastguard Worker                    numbytes = nti(buf[pos + 12:pos + 24])
1263*cda5da8dSAndroid Build Coastguard Worker                except ValueError:
1264*cda5da8dSAndroid Build Coastguard Worker                    break
1265*cda5da8dSAndroid Build Coastguard Worker                structs.append((offset, numbytes))
1266*cda5da8dSAndroid Build Coastguard Worker                pos += 24
1267*cda5da8dSAndroid Build Coastguard Worker            isextended = bool(buf[482])
1268*cda5da8dSAndroid Build Coastguard Worker            origsize = nti(buf[483:495])
1269*cda5da8dSAndroid Build Coastguard Worker            obj._sparse_structs = (structs, isextended, origsize)
1270*cda5da8dSAndroid Build Coastguard Worker
1271*cda5da8dSAndroid Build Coastguard Worker        # Remove redundant slashes from directories.
1272*cda5da8dSAndroid Build Coastguard Worker        if obj.isdir():
1273*cda5da8dSAndroid Build Coastguard Worker            obj.name = obj.name.rstrip("/")
1274*cda5da8dSAndroid Build Coastguard Worker
1275*cda5da8dSAndroid Build Coastguard Worker        # Reconstruct a ustar longname.
1276*cda5da8dSAndroid Build Coastguard Worker        if prefix and obj.type not in GNU_TYPES:
1277*cda5da8dSAndroid Build Coastguard Worker            obj.name = prefix + "/" + obj.name
1278*cda5da8dSAndroid Build Coastguard Worker        return obj
1279*cda5da8dSAndroid Build Coastguard Worker
1280*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1281*cda5da8dSAndroid Build Coastguard Worker    def fromtarfile(cls, tarfile):
1282*cda5da8dSAndroid Build Coastguard Worker        """Return the next TarInfo object from TarFile object
1283*cda5da8dSAndroid Build Coastguard Worker           tarfile.
1284*cda5da8dSAndroid Build Coastguard Worker        """
1285*cda5da8dSAndroid Build Coastguard Worker        buf = tarfile.fileobj.read(BLOCKSIZE)
1286*cda5da8dSAndroid Build Coastguard Worker        obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors)
1287*cda5da8dSAndroid Build Coastguard Worker        obj.offset = tarfile.fileobj.tell() - BLOCKSIZE
1288*cda5da8dSAndroid Build Coastguard Worker        return obj._proc_member(tarfile)
1289*cda5da8dSAndroid Build Coastguard Worker
1290*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
1291*cda5da8dSAndroid Build Coastguard Worker    # The following are methods that are called depending on the type of a
1292*cda5da8dSAndroid Build Coastguard Worker    # member. The entry point is _proc_member() which can be overridden in a
1293*cda5da8dSAndroid Build Coastguard Worker    # subclass to add custom _proc_*() methods. A _proc_*() method MUST
1294*cda5da8dSAndroid Build Coastguard Worker    # implement the following
1295*cda5da8dSAndroid Build Coastguard Worker    # operations:
1296*cda5da8dSAndroid Build Coastguard Worker    # 1. Set self.offset_data to the position where the data blocks begin,
1297*cda5da8dSAndroid Build Coastguard Worker    #    if there is data that follows.
1298*cda5da8dSAndroid Build Coastguard Worker    # 2. Set tarfile.offset to the position where the next member's header will
1299*cda5da8dSAndroid Build Coastguard Worker    #    begin.
1300*cda5da8dSAndroid Build Coastguard Worker    # 3. Return self or another valid TarInfo object.
1301*cda5da8dSAndroid Build Coastguard Worker    def _proc_member(self, tarfile):
1302*cda5da8dSAndroid Build Coastguard Worker        """Choose the right processing method depending on
1303*cda5da8dSAndroid Build Coastguard Worker           the type and call it.
1304*cda5da8dSAndroid Build Coastguard Worker        """
1305*cda5da8dSAndroid Build Coastguard Worker        if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK):
1306*cda5da8dSAndroid Build Coastguard Worker            return self._proc_gnulong(tarfile)
1307*cda5da8dSAndroid Build Coastguard Worker        elif self.type == GNUTYPE_SPARSE:
1308*cda5da8dSAndroid Build Coastguard Worker            return self._proc_sparse(tarfile)
1309*cda5da8dSAndroid Build Coastguard Worker        elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE):
1310*cda5da8dSAndroid Build Coastguard Worker            return self._proc_pax(tarfile)
1311*cda5da8dSAndroid Build Coastguard Worker        else:
1312*cda5da8dSAndroid Build Coastguard Worker            return self._proc_builtin(tarfile)
1313*cda5da8dSAndroid Build Coastguard Worker
1314*cda5da8dSAndroid Build Coastguard Worker    def _proc_builtin(self, tarfile):
1315*cda5da8dSAndroid Build Coastguard Worker        """Process a builtin type or an unknown type which
1316*cda5da8dSAndroid Build Coastguard Worker           will be treated as a regular file.
1317*cda5da8dSAndroid Build Coastguard Worker        """
1318*cda5da8dSAndroid Build Coastguard Worker        self.offset_data = tarfile.fileobj.tell()
1319*cda5da8dSAndroid Build Coastguard Worker        offset = self.offset_data
1320*cda5da8dSAndroid Build Coastguard Worker        if self.isreg() or self.type not in SUPPORTED_TYPES:
1321*cda5da8dSAndroid Build Coastguard Worker            # Skip the following data blocks.
1322*cda5da8dSAndroid Build Coastguard Worker            offset += self._block(self.size)
1323*cda5da8dSAndroid Build Coastguard Worker        tarfile.offset = offset
1324*cda5da8dSAndroid Build Coastguard Worker
1325*cda5da8dSAndroid Build Coastguard Worker        # Patch the TarInfo object with saved global
1326*cda5da8dSAndroid Build Coastguard Worker        # header information.
1327*cda5da8dSAndroid Build Coastguard Worker        self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors)
1328*cda5da8dSAndroid Build Coastguard Worker
1329*cda5da8dSAndroid Build Coastguard Worker        # Remove redundant slashes from directories. This is to be consistent
1330*cda5da8dSAndroid Build Coastguard Worker        # with frombuf().
1331*cda5da8dSAndroid Build Coastguard Worker        if self.isdir():
1332*cda5da8dSAndroid Build Coastguard Worker            self.name = self.name.rstrip("/")
1333*cda5da8dSAndroid Build Coastguard Worker
1334*cda5da8dSAndroid Build Coastguard Worker        return self
1335*cda5da8dSAndroid Build Coastguard Worker
1336*cda5da8dSAndroid Build Coastguard Worker    def _proc_gnulong(self, tarfile):
1337*cda5da8dSAndroid Build Coastguard Worker        """Process the blocks that hold a GNU longname
1338*cda5da8dSAndroid Build Coastguard Worker           or longlink member.
1339*cda5da8dSAndroid Build Coastguard Worker        """
1340*cda5da8dSAndroid Build Coastguard Worker        buf = tarfile.fileobj.read(self._block(self.size))
1341*cda5da8dSAndroid Build Coastguard Worker
1342*cda5da8dSAndroid Build Coastguard Worker        # Fetch the next header and process it.
1343*cda5da8dSAndroid Build Coastguard Worker        try:
1344*cda5da8dSAndroid Build Coastguard Worker            next = self.fromtarfile(tarfile)
1345*cda5da8dSAndroid Build Coastguard Worker        except HeaderError as e:
1346*cda5da8dSAndroid Build Coastguard Worker            raise SubsequentHeaderError(str(e)) from None
1347*cda5da8dSAndroid Build Coastguard Worker
1348*cda5da8dSAndroid Build Coastguard Worker        # Patch the TarInfo object from the next header with
1349*cda5da8dSAndroid Build Coastguard Worker        # the longname information.
1350*cda5da8dSAndroid Build Coastguard Worker        next.offset = self.offset
1351*cda5da8dSAndroid Build Coastguard Worker        if self.type == GNUTYPE_LONGNAME:
1352*cda5da8dSAndroid Build Coastguard Worker            next.name = nts(buf, tarfile.encoding, tarfile.errors)
1353*cda5da8dSAndroid Build Coastguard Worker        elif self.type == GNUTYPE_LONGLINK:
1354*cda5da8dSAndroid Build Coastguard Worker            next.linkname = nts(buf, tarfile.encoding, tarfile.errors)
1355*cda5da8dSAndroid Build Coastguard Worker
1356*cda5da8dSAndroid Build Coastguard Worker        # Remove redundant slashes from directories. This is to be consistent
1357*cda5da8dSAndroid Build Coastguard Worker        # with frombuf().
1358*cda5da8dSAndroid Build Coastguard Worker        if next.isdir():
1359*cda5da8dSAndroid Build Coastguard Worker            next.name = next.name.removesuffix("/")
1360*cda5da8dSAndroid Build Coastguard Worker
1361*cda5da8dSAndroid Build Coastguard Worker        return next
1362*cda5da8dSAndroid Build Coastguard Worker
1363*cda5da8dSAndroid Build Coastguard Worker    def _proc_sparse(self, tarfile):
1364*cda5da8dSAndroid Build Coastguard Worker        """Process a GNU sparse header plus extra headers.
1365*cda5da8dSAndroid Build Coastguard Worker        """
1366*cda5da8dSAndroid Build Coastguard Worker        # We already collected some sparse structures in frombuf().
1367*cda5da8dSAndroid Build Coastguard Worker        structs, isextended, origsize = self._sparse_structs
1368*cda5da8dSAndroid Build Coastguard Worker        del self._sparse_structs
1369*cda5da8dSAndroid Build Coastguard Worker
1370*cda5da8dSAndroid Build Coastguard Worker        # Collect sparse structures from extended header blocks.
1371*cda5da8dSAndroid Build Coastguard Worker        while isextended:
1372*cda5da8dSAndroid Build Coastguard Worker            buf = tarfile.fileobj.read(BLOCKSIZE)
1373*cda5da8dSAndroid Build Coastguard Worker            pos = 0
1374*cda5da8dSAndroid Build Coastguard Worker            for i in range(21):
1375*cda5da8dSAndroid Build Coastguard Worker                try:
1376*cda5da8dSAndroid Build Coastguard Worker                    offset = nti(buf[pos:pos + 12])
1377*cda5da8dSAndroid Build Coastguard Worker                    numbytes = nti(buf[pos + 12:pos + 24])
1378*cda5da8dSAndroid Build Coastguard Worker                except ValueError:
1379*cda5da8dSAndroid Build Coastguard Worker                    break
1380*cda5da8dSAndroid Build Coastguard Worker                if offset and numbytes:
1381*cda5da8dSAndroid Build Coastguard Worker                    structs.append((offset, numbytes))
1382*cda5da8dSAndroid Build Coastguard Worker                pos += 24
1383*cda5da8dSAndroid Build Coastguard Worker            isextended = bool(buf[504])
1384*cda5da8dSAndroid Build Coastguard Worker        self.sparse = structs
1385*cda5da8dSAndroid Build Coastguard Worker
1386*cda5da8dSAndroid Build Coastguard Worker        self.offset_data = tarfile.fileobj.tell()
1387*cda5da8dSAndroid Build Coastguard Worker        tarfile.offset = self.offset_data + self._block(self.size)
1388*cda5da8dSAndroid Build Coastguard Worker        self.size = origsize
1389*cda5da8dSAndroid Build Coastguard Worker        return self
1390*cda5da8dSAndroid Build Coastguard Worker
1391*cda5da8dSAndroid Build Coastguard Worker    def _proc_pax(self, tarfile):
1392*cda5da8dSAndroid Build Coastguard Worker        """Process an extended or global header as described in
1393*cda5da8dSAndroid Build Coastguard Worker           POSIX.1-2008.
1394*cda5da8dSAndroid Build Coastguard Worker        """
1395*cda5da8dSAndroid Build Coastguard Worker        # Read the header information.
1396*cda5da8dSAndroid Build Coastguard Worker        buf = tarfile.fileobj.read(self._block(self.size))
1397*cda5da8dSAndroid Build Coastguard Worker
1398*cda5da8dSAndroid Build Coastguard Worker        # A pax header stores supplemental information for either
1399*cda5da8dSAndroid Build Coastguard Worker        # the following file (extended) or all following files
1400*cda5da8dSAndroid Build Coastguard Worker        # (global).
1401*cda5da8dSAndroid Build Coastguard Worker        if self.type == XGLTYPE:
1402*cda5da8dSAndroid Build Coastguard Worker            pax_headers = tarfile.pax_headers
1403*cda5da8dSAndroid Build Coastguard Worker        else:
1404*cda5da8dSAndroid Build Coastguard Worker            pax_headers = tarfile.pax_headers.copy()
1405*cda5da8dSAndroid Build Coastguard Worker
1406*cda5da8dSAndroid Build Coastguard Worker        # Check if the pax header contains a hdrcharset field. This tells us
1407*cda5da8dSAndroid Build Coastguard Worker        # the encoding of the path, linkpath, uname and gname fields. Normally,
1408*cda5da8dSAndroid Build Coastguard Worker        # these fields are UTF-8 encoded but since POSIX.1-2008 tar
1409*cda5da8dSAndroid Build Coastguard Worker        # implementations are allowed to store them as raw binary strings if
1410*cda5da8dSAndroid Build Coastguard Worker        # the translation to UTF-8 fails.
1411*cda5da8dSAndroid Build Coastguard Worker        match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf)
1412*cda5da8dSAndroid Build Coastguard Worker        if match is not None:
1413*cda5da8dSAndroid Build Coastguard Worker            pax_headers["hdrcharset"] = match.group(1).decode("utf-8")
1414*cda5da8dSAndroid Build Coastguard Worker
1415*cda5da8dSAndroid Build Coastguard Worker        # For the time being, we don't care about anything other than "BINARY".
1416*cda5da8dSAndroid Build Coastguard Worker        # The only other value that is currently allowed by the standard is
1417*cda5da8dSAndroid Build Coastguard Worker        # "ISO-IR 10646 2000 UTF-8" in other words UTF-8.
1418*cda5da8dSAndroid Build Coastguard Worker        hdrcharset = pax_headers.get("hdrcharset")
1419*cda5da8dSAndroid Build Coastguard Worker        if hdrcharset == "BINARY":
1420*cda5da8dSAndroid Build Coastguard Worker            encoding = tarfile.encoding
1421*cda5da8dSAndroid Build Coastguard Worker        else:
1422*cda5da8dSAndroid Build Coastguard Worker            encoding = "utf-8"
1423*cda5da8dSAndroid Build Coastguard Worker
1424*cda5da8dSAndroid Build Coastguard Worker        # Parse pax header information. A record looks like that:
1425*cda5da8dSAndroid Build Coastguard Worker        # "%d %s=%s\n" % (length, keyword, value). length is the size
1426*cda5da8dSAndroid Build Coastguard Worker        # of the complete record including the length field itself and
1427*cda5da8dSAndroid Build Coastguard Worker        # the newline. keyword and value are both UTF-8 encoded strings.
1428*cda5da8dSAndroid Build Coastguard Worker        regex = re.compile(br"(\d+) ([^=]+)=")
1429*cda5da8dSAndroid Build Coastguard Worker        pos = 0
1430*cda5da8dSAndroid Build Coastguard Worker        while True:
1431*cda5da8dSAndroid Build Coastguard Worker            match = regex.match(buf, pos)
1432*cda5da8dSAndroid Build Coastguard Worker            if not match:
1433*cda5da8dSAndroid Build Coastguard Worker                break
1434*cda5da8dSAndroid Build Coastguard Worker
1435*cda5da8dSAndroid Build Coastguard Worker            length, keyword = match.groups()
1436*cda5da8dSAndroid Build Coastguard Worker            length = int(length)
1437*cda5da8dSAndroid Build Coastguard Worker            if length == 0:
1438*cda5da8dSAndroid Build Coastguard Worker                raise InvalidHeaderError("invalid header")
1439*cda5da8dSAndroid Build Coastguard Worker            value = buf[match.end(2) + 1:match.start(1) + length - 1]
1440*cda5da8dSAndroid Build Coastguard Worker
1441*cda5da8dSAndroid Build Coastguard Worker            # Normally, we could just use "utf-8" as the encoding and "strict"
1442*cda5da8dSAndroid Build Coastguard Worker            # as the error handler, but we better not take the risk. For
1443*cda5da8dSAndroid Build Coastguard Worker            # example, GNU tar <= 1.23 is known to store filenames it cannot
1444*cda5da8dSAndroid Build Coastguard Worker            # translate to UTF-8 as raw strings (unfortunately without a
1445*cda5da8dSAndroid Build Coastguard Worker            # hdrcharset=BINARY header).
1446*cda5da8dSAndroid Build Coastguard Worker            # We first try the strict standard encoding, and if that fails we
1447*cda5da8dSAndroid Build Coastguard Worker            # fall back on the user's encoding and error handler.
1448*cda5da8dSAndroid Build Coastguard Worker            keyword = self._decode_pax_field(keyword, "utf-8", "utf-8",
1449*cda5da8dSAndroid Build Coastguard Worker                    tarfile.errors)
1450*cda5da8dSAndroid Build Coastguard Worker            if keyword in PAX_NAME_FIELDS:
1451*cda5da8dSAndroid Build Coastguard Worker                value = self._decode_pax_field(value, encoding, tarfile.encoding,
1452*cda5da8dSAndroid Build Coastguard Worker                        tarfile.errors)
1453*cda5da8dSAndroid Build Coastguard Worker            else:
1454*cda5da8dSAndroid Build Coastguard Worker                value = self._decode_pax_field(value, "utf-8", "utf-8",
1455*cda5da8dSAndroid Build Coastguard Worker                        tarfile.errors)
1456*cda5da8dSAndroid Build Coastguard Worker
1457*cda5da8dSAndroid Build Coastguard Worker            pax_headers[keyword] = value
1458*cda5da8dSAndroid Build Coastguard Worker            pos += length
1459*cda5da8dSAndroid Build Coastguard Worker
1460*cda5da8dSAndroid Build Coastguard Worker        # Fetch the next header.
1461*cda5da8dSAndroid Build Coastguard Worker        try:
1462*cda5da8dSAndroid Build Coastguard Worker            next = self.fromtarfile(tarfile)
1463*cda5da8dSAndroid Build Coastguard Worker        except HeaderError as e:
1464*cda5da8dSAndroid Build Coastguard Worker            raise SubsequentHeaderError(str(e)) from None
1465*cda5da8dSAndroid Build Coastguard Worker
1466*cda5da8dSAndroid Build Coastguard Worker        # Process GNU sparse information.
1467*cda5da8dSAndroid Build Coastguard Worker        if "GNU.sparse.map" in pax_headers:
1468*cda5da8dSAndroid Build Coastguard Worker            # GNU extended sparse format version 0.1.
1469*cda5da8dSAndroid Build Coastguard Worker            self._proc_gnusparse_01(next, pax_headers)
1470*cda5da8dSAndroid Build Coastguard Worker
1471*cda5da8dSAndroid Build Coastguard Worker        elif "GNU.sparse.size" in pax_headers:
1472*cda5da8dSAndroid Build Coastguard Worker            # GNU extended sparse format version 0.0.
1473*cda5da8dSAndroid Build Coastguard Worker            self._proc_gnusparse_00(next, pax_headers, buf)
1474*cda5da8dSAndroid Build Coastguard Worker
1475*cda5da8dSAndroid Build Coastguard Worker        elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0":
1476*cda5da8dSAndroid Build Coastguard Worker            # GNU extended sparse format version 1.0.
1477*cda5da8dSAndroid Build Coastguard Worker            self._proc_gnusparse_10(next, pax_headers, tarfile)
1478*cda5da8dSAndroid Build Coastguard Worker
1479*cda5da8dSAndroid Build Coastguard Worker        if self.type in (XHDTYPE, SOLARIS_XHDTYPE):
1480*cda5da8dSAndroid Build Coastguard Worker            # Patch the TarInfo object with the extended header info.
1481*cda5da8dSAndroid Build Coastguard Worker            next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors)
1482*cda5da8dSAndroid Build Coastguard Worker            next.offset = self.offset
1483*cda5da8dSAndroid Build Coastguard Worker
1484*cda5da8dSAndroid Build Coastguard Worker            if "size" in pax_headers:
1485*cda5da8dSAndroid Build Coastguard Worker                # If the extended header replaces the size field,
1486*cda5da8dSAndroid Build Coastguard Worker                # we need to recalculate the offset where the next
1487*cda5da8dSAndroid Build Coastguard Worker                # header starts.
1488*cda5da8dSAndroid Build Coastguard Worker                offset = next.offset_data
1489*cda5da8dSAndroid Build Coastguard Worker                if next.isreg() or next.type not in SUPPORTED_TYPES:
1490*cda5da8dSAndroid Build Coastguard Worker                    offset += next._block(next.size)
1491*cda5da8dSAndroid Build Coastguard Worker                tarfile.offset = offset
1492*cda5da8dSAndroid Build Coastguard Worker
1493*cda5da8dSAndroid Build Coastguard Worker        return next
1494*cda5da8dSAndroid Build Coastguard Worker
1495*cda5da8dSAndroid Build Coastguard Worker    def _proc_gnusparse_00(self, next, pax_headers, buf):
1496*cda5da8dSAndroid Build Coastguard Worker        """Process a GNU tar extended sparse header, version 0.0.
1497*cda5da8dSAndroid Build Coastguard Worker        """
1498*cda5da8dSAndroid Build Coastguard Worker        offsets = []
1499*cda5da8dSAndroid Build Coastguard Worker        for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf):
1500*cda5da8dSAndroid Build Coastguard Worker            offsets.append(int(match.group(1)))
1501*cda5da8dSAndroid Build Coastguard Worker        numbytes = []
1502*cda5da8dSAndroid Build Coastguard Worker        for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf):
1503*cda5da8dSAndroid Build Coastguard Worker            numbytes.append(int(match.group(1)))
1504*cda5da8dSAndroid Build Coastguard Worker        next.sparse = list(zip(offsets, numbytes))
1505*cda5da8dSAndroid Build Coastguard Worker
1506*cda5da8dSAndroid Build Coastguard Worker    def _proc_gnusparse_01(self, next, pax_headers):
1507*cda5da8dSAndroid Build Coastguard Worker        """Process a GNU tar extended sparse header, version 0.1.
1508*cda5da8dSAndroid Build Coastguard Worker        """
1509*cda5da8dSAndroid Build Coastguard Worker        sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")]
1510*cda5da8dSAndroid Build Coastguard Worker        next.sparse = list(zip(sparse[::2], sparse[1::2]))
1511*cda5da8dSAndroid Build Coastguard Worker
1512*cda5da8dSAndroid Build Coastguard Worker    def _proc_gnusparse_10(self, next, pax_headers, tarfile):
1513*cda5da8dSAndroid Build Coastguard Worker        """Process a GNU tar extended sparse header, version 1.0.
1514*cda5da8dSAndroid Build Coastguard Worker        """
1515*cda5da8dSAndroid Build Coastguard Worker        fields = None
1516*cda5da8dSAndroid Build Coastguard Worker        sparse = []
1517*cda5da8dSAndroid Build Coastguard Worker        buf = tarfile.fileobj.read(BLOCKSIZE)
1518*cda5da8dSAndroid Build Coastguard Worker        fields, buf = buf.split(b"\n", 1)
1519*cda5da8dSAndroid Build Coastguard Worker        fields = int(fields)
1520*cda5da8dSAndroid Build Coastguard Worker        while len(sparse) < fields * 2:
1521*cda5da8dSAndroid Build Coastguard Worker            if b"\n" not in buf:
1522*cda5da8dSAndroid Build Coastguard Worker                buf += tarfile.fileobj.read(BLOCKSIZE)
1523*cda5da8dSAndroid Build Coastguard Worker            number, buf = buf.split(b"\n", 1)
1524*cda5da8dSAndroid Build Coastguard Worker            sparse.append(int(number))
1525*cda5da8dSAndroid Build Coastguard Worker        next.offset_data = tarfile.fileobj.tell()
1526*cda5da8dSAndroid Build Coastguard Worker        next.sparse = list(zip(sparse[::2], sparse[1::2]))
1527*cda5da8dSAndroid Build Coastguard Worker
1528*cda5da8dSAndroid Build Coastguard Worker    def _apply_pax_info(self, pax_headers, encoding, errors):
1529*cda5da8dSAndroid Build Coastguard Worker        """Replace fields with supplemental information from a previous
1530*cda5da8dSAndroid Build Coastguard Worker           pax extended or global header.
1531*cda5da8dSAndroid Build Coastguard Worker        """
1532*cda5da8dSAndroid Build Coastguard Worker        for keyword, value in pax_headers.items():
1533*cda5da8dSAndroid Build Coastguard Worker            if keyword == "GNU.sparse.name":
1534*cda5da8dSAndroid Build Coastguard Worker                setattr(self, "path", value)
1535*cda5da8dSAndroid Build Coastguard Worker            elif keyword == "GNU.sparse.size":
1536*cda5da8dSAndroid Build Coastguard Worker                setattr(self, "size", int(value))
1537*cda5da8dSAndroid Build Coastguard Worker            elif keyword == "GNU.sparse.realsize":
1538*cda5da8dSAndroid Build Coastguard Worker                setattr(self, "size", int(value))
1539*cda5da8dSAndroid Build Coastguard Worker            elif keyword in PAX_FIELDS:
1540*cda5da8dSAndroid Build Coastguard Worker                if keyword in PAX_NUMBER_FIELDS:
1541*cda5da8dSAndroid Build Coastguard Worker                    try:
1542*cda5da8dSAndroid Build Coastguard Worker                        value = PAX_NUMBER_FIELDS[keyword](value)
1543*cda5da8dSAndroid Build Coastguard Worker                    except ValueError:
1544*cda5da8dSAndroid Build Coastguard Worker                        value = 0
1545*cda5da8dSAndroid Build Coastguard Worker                if keyword == "path":
1546*cda5da8dSAndroid Build Coastguard Worker                    value = value.rstrip("/")
1547*cda5da8dSAndroid Build Coastguard Worker                setattr(self, keyword, value)
1548*cda5da8dSAndroid Build Coastguard Worker
1549*cda5da8dSAndroid Build Coastguard Worker        self.pax_headers = pax_headers.copy()
1550*cda5da8dSAndroid Build Coastguard Worker
1551*cda5da8dSAndroid Build Coastguard Worker    def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors):
1552*cda5da8dSAndroid Build Coastguard Worker        """Decode a single field from a pax record.
1553*cda5da8dSAndroid Build Coastguard Worker        """
1554*cda5da8dSAndroid Build Coastguard Worker        try:
1555*cda5da8dSAndroid Build Coastguard Worker            return value.decode(encoding, "strict")
1556*cda5da8dSAndroid Build Coastguard Worker        except UnicodeDecodeError:
1557*cda5da8dSAndroid Build Coastguard Worker            return value.decode(fallback_encoding, fallback_errors)
1558*cda5da8dSAndroid Build Coastguard Worker
1559*cda5da8dSAndroid Build Coastguard Worker    def _block(self, count):
1560*cda5da8dSAndroid Build Coastguard Worker        """Round up a byte count by BLOCKSIZE and return it,
1561*cda5da8dSAndroid Build Coastguard Worker           e.g. _block(834) => 1024.
1562*cda5da8dSAndroid Build Coastguard Worker        """
1563*cda5da8dSAndroid Build Coastguard Worker        blocks, remainder = divmod(count, BLOCKSIZE)
1564*cda5da8dSAndroid Build Coastguard Worker        if remainder:
1565*cda5da8dSAndroid Build Coastguard Worker            blocks += 1
1566*cda5da8dSAndroid Build Coastguard Worker        return blocks * BLOCKSIZE
1567*cda5da8dSAndroid Build Coastguard Worker
1568*cda5da8dSAndroid Build Coastguard Worker    def isreg(self):
1569*cda5da8dSAndroid Build Coastguard Worker        'Return True if the Tarinfo object is a regular file.'
1570*cda5da8dSAndroid Build Coastguard Worker        return self.type in REGULAR_TYPES
1571*cda5da8dSAndroid Build Coastguard Worker
1572*cda5da8dSAndroid Build Coastguard Worker    def isfile(self):
1573*cda5da8dSAndroid Build Coastguard Worker        'Return True if the Tarinfo object is a regular file.'
1574*cda5da8dSAndroid Build Coastguard Worker        return self.isreg()
1575*cda5da8dSAndroid Build Coastguard Worker
1576*cda5da8dSAndroid Build Coastguard Worker    def isdir(self):
1577*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a directory.'
1578*cda5da8dSAndroid Build Coastguard Worker        return self.type == DIRTYPE
1579*cda5da8dSAndroid Build Coastguard Worker
1580*cda5da8dSAndroid Build Coastguard Worker    def issym(self):
1581*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a symbolic link.'
1582*cda5da8dSAndroid Build Coastguard Worker        return self.type == SYMTYPE
1583*cda5da8dSAndroid Build Coastguard Worker
1584*cda5da8dSAndroid Build Coastguard Worker    def islnk(self):
1585*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a hard link.'
1586*cda5da8dSAndroid Build Coastguard Worker        return self.type == LNKTYPE
1587*cda5da8dSAndroid Build Coastguard Worker
1588*cda5da8dSAndroid Build Coastguard Worker    def ischr(self):
1589*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a character device.'
1590*cda5da8dSAndroid Build Coastguard Worker        return self.type == CHRTYPE
1591*cda5da8dSAndroid Build Coastguard Worker
1592*cda5da8dSAndroid Build Coastguard Worker    def isblk(self):
1593*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a block device.'
1594*cda5da8dSAndroid Build Coastguard Worker        return self.type == BLKTYPE
1595*cda5da8dSAndroid Build Coastguard Worker
1596*cda5da8dSAndroid Build Coastguard Worker    def isfifo(self):
1597*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is a FIFO.'
1598*cda5da8dSAndroid Build Coastguard Worker        return self.type == FIFOTYPE
1599*cda5da8dSAndroid Build Coastguard Worker
1600*cda5da8dSAndroid Build Coastguard Worker    def issparse(self):
1601*cda5da8dSAndroid Build Coastguard Worker        return self.sparse is not None
1602*cda5da8dSAndroid Build Coastguard Worker
1603*cda5da8dSAndroid Build Coastguard Worker    def isdev(self):
1604*cda5da8dSAndroid Build Coastguard Worker        'Return True if it is one of character device, block device or FIFO.'
1605*cda5da8dSAndroid Build Coastguard Worker        return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE)
1606*cda5da8dSAndroid Build Coastguard Worker# class TarInfo
1607*cda5da8dSAndroid Build Coastguard Worker
1608*cda5da8dSAndroid Build Coastguard Workerclass TarFile(object):
1609*cda5da8dSAndroid Build Coastguard Worker    """The TarFile Class provides an interface to tar archives.
1610*cda5da8dSAndroid Build Coastguard Worker    """
1611*cda5da8dSAndroid Build Coastguard Worker
1612*cda5da8dSAndroid Build Coastguard Worker    debug = 0                   # May be set from 0 (no msgs) to 3 (all msgs)
1613*cda5da8dSAndroid Build Coastguard Worker
1614*cda5da8dSAndroid Build Coastguard Worker    dereference = False         # If true, add content of linked file to the
1615*cda5da8dSAndroid Build Coastguard Worker                                # tar file, else the link.
1616*cda5da8dSAndroid Build Coastguard Worker
1617*cda5da8dSAndroid Build Coastguard Worker    ignore_zeros = False        # If true, skips empty or invalid blocks and
1618*cda5da8dSAndroid Build Coastguard Worker                                # continues processing.
1619*cda5da8dSAndroid Build Coastguard Worker
1620*cda5da8dSAndroid Build Coastguard Worker    errorlevel = 1              # If 0, fatal errors only appear in debug
1621*cda5da8dSAndroid Build Coastguard Worker                                # messages (if debug >= 0). If > 0, errors
1622*cda5da8dSAndroid Build Coastguard Worker                                # are passed to the caller as exceptions.
1623*cda5da8dSAndroid Build Coastguard Worker
1624*cda5da8dSAndroid Build Coastguard Worker    format = DEFAULT_FORMAT     # The format to use when creating an archive.
1625*cda5da8dSAndroid Build Coastguard Worker
1626*cda5da8dSAndroid Build Coastguard Worker    encoding = ENCODING         # Encoding for 8-bit character strings.
1627*cda5da8dSAndroid Build Coastguard Worker
1628*cda5da8dSAndroid Build Coastguard Worker    errors = None               # Error handler for unicode conversion.
1629*cda5da8dSAndroid Build Coastguard Worker
1630*cda5da8dSAndroid Build Coastguard Worker    tarinfo = TarInfo           # The default TarInfo class to use.
1631*cda5da8dSAndroid Build Coastguard Worker
1632*cda5da8dSAndroid Build Coastguard Worker    fileobject = ExFileObject   # The file-object for extractfile().
1633*cda5da8dSAndroid Build Coastguard Worker
1634*cda5da8dSAndroid Build Coastguard Worker    extraction_filter = None    # The default filter for extraction.
1635*cda5da8dSAndroid Build Coastguard Worker
1636*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, name=None, mode="r", fileobj=None, format=None,
1637*cda5da8dSAndroid Build Coastguard Worker            tarinfo=None, dereference=None, ignore_zeros=None, encoding=None,
1638*cda5da8dSAndroid Build Coastguard Worker            errors="surrogateescape", pax_headers=None, debug=None,
1639*cda5da8dSAndroid Build Coastguard Worker            errorlevel=None, copybufsize=None):
1640*cda5da8dSAndroid Build Coastguard Worker        """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to
1641*cda5da8dSAndroid Build Coastguard Worker           read from an existing archive, 'a' to append data to an existing
1642*cda5da8dSAndroid Build Coastguard Worker           file or 'w' to create a new file overwriting an existing one. `mode'
1643*cda5da8dSAndroid Build Coastguard Worker           defaults to 'r'.
1644*cda5da8dSAndroid Build Coastguard Worker           If `fileobj' is given, it is used for reading or writing data. If it
1645*cda5da8dSAndroid Build Coastguard Worker           can be determined, `mode' is overridden by `fileobj's mode.
1646*cda5da8dSAndroid Build Coastguard Worker           `fileobj' is not closed, when TarFile is closed.
1647*cda5da8dSAndroid Build Coastguard Worker        """
1648*cda5da8dSAndroid Build Coastguard Worker        modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"}
1649*cda5da8dSAndroid Build Coastguard Worker        if mode not in modes:
1650*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
1651*cda5da8dSAndroid Build Coastguard Worker        self.mode = mode
1652*cda5da8dSAndroid Build Coastguard Worker        self._mode = modes[mode]
1653*cda5da8dSAndroid Build Coastguard Worker
1654*cda5da8dSAndroid Build Coastguard Worker        if not fileobj:
1655*cda5da8dSAndroid Build Coastguard Worker            if self.mode == "a" and not os.path.exists(name):
1656*cda5da8dSAndroid Build Coastguard Worker                # Create nonexistent files in append mode.
1657*cda5da8dSAndroid Build Coastguard Worker                self.mode = "w"
1658*cda5da8dSAndroid Build Coastguard Worker                self._mode = "wb"
1659*cda5da8dSAndroid Build Coastguard Worker            fileobj = bltn_open(name, self._mode)
1660*cda5da8dSAndroid Build Coastguard Worker            self._extfileobj = False
1661*cda5da8dSAndroid Build Coastguard Worker        else:
1662*cda5da8dSAndroid Build Coastguard Worker            if (name is None and hasattr(fileobj, "name") and
1663*cda5da8dSAndroid Build Coastguard Worker                isinstance(fileobj.name, (str, bytes))):
1664*cda5da8dSAndroid Build Coastguard Worker                name = fileobj.name
1665*cda5da8dSAndroid Build Coastguard Worker            if hasattr(fileobj, "mode"):
1666*cda5da8dSAndroid Build Coastguard Worker                self._mode = fileobj.mode
1667*cda5da8dSAndroid Build Coastguard Worker            self._extfileobj = True
1668*cda5da8dSAndroid Build Coastguard Worker        self.name = os.path.abspath(name) if name else None
1669*cda5da8dSAndroid Build Coastguard Worker        self.fileobj = fileobj
1670*cda5da8dSAndroid Build Coastguard Worker
1671*cda5da8dSAndroid Build Coastguard Worker        # Init attributes.
1672*cda5da8dSAndroid Build Coastguard Worker        if format is not None:
1673*cda5da8dSAndroid Build Coastguard Worker            self.format = format
1674*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is not None:
1675*cda5da8dSAndroid Build Coastguard Worker            self.tarinfo = tarinfo
1676*cda5da8dSAndroid Build Coastguard Worker        if dereference is not None:
1677*cda5da8dSAndroid Build Coastguard Worker            self.dereference = dereference
1678*cda5da8dSAndroid Build Coastguard Worker        if ignore_zeros is not None:
1679*cda5da8dSAndroid Build Coastguard Worker            self.ignore_zeros = ignore_zeros
1680*cda5da8dSAndroid Build Coastguard Worker        if encoding is not None:
1681*cda5da8dSAndroid Build Coastguard Worker            self.encoding = encoding
1682*cda5da8dSAndroid Build Coastguard Worker        self.errors = errors
1683*cda5da8dSAndroid Build Coastguard Worker
1684*cda5da8dSAndroid Build Coastguard Worker        if pax_headers is not None and self.format == PAX_FORMAT:
1685*cda5da8dSAndroid Build Coastguard Worker            self.pax_headers = pax_headers
1686*cda5da8dSAndroid Build Coastguard Worker        else:
1687*cda5da8dSAndroid Build Coastguard Worker            self.pax_headers = {}
1688*cda5da8dSAndroid Build Coastguard Worker
1689*cda5da8dSAndroid Build Coastguard Worker        if debug is not None:
1690*cda5da8dSAndroid Build Coastguard Worker            self.debug = debug
1691*cda5da8dSAndroid Build Coastguard Worker        if errorlevel is not None:
1692*cda5da8dSAndroid Build Coastguard Worker            self.errorlevel = errorlevel
1693*cda5da8dSAndroid Build Coastguard Worker
1694*cda5da8dSAndroid Build Coastguard Worker        # Init datastructures.
1695*cda5da8dSAndroid Build Coastguard Worker        self.copybufsize = copybufsize
1696*cda5da8dSAndroid Build Coastguard Worker        self.closed = False
1697*cda5da8dSAndroid Build Coastguard Worker        self.members = []       # list of members as TarInfo objects
1698*cda5da8dSAndroid Build Coastguard Worker        self._loaded = False    # flag if all members have been read
1699*cda5da8dSAndroid Build Coastguard Worker        self.offset = self.fileobj.tell()
1700*cda5da8dSAndroid Build Coastguard Worker                                # current position in the archive file
1701*cda5da8dSAndroid Build Coastguard Worker        self.inodes = {}        # dictionary caching the inodes of
1702*cda5da8dSAndroid Build Coastguard Worker                                # archive members already added
1703*cda5da8dSAndroid Build Coastguard Worker
1704*cda5da8dSAndroid Build Coastguard Worker        try:
1705*cda5da8dSAndroid Build Coastguard Worker            if self.mode == "r":
1706*cda5da8dSAndroid Build Coastguard Worker                self.firstmember = None
1707*cda5da8dSAndroid Build Coastguard Worker                self.firstmember = self.next()
1708*cda5da8dSAndroid Build Coastguard Worker
1709*cda5da8dSAndroid Build Coastguard Worker            if self.mode == "a":
1710*cda5da8dSAndroid Build Coastguard Worker                # Move to the end of the archive,
1711*cda5da8dSAndroid Build Coastguard Worker                # before the first empty block.
1712*cda5da8dSAndroid Build Coastguard Worker                while True:
1713*cda5da8dSAndroid Build Coastguard Worker                    self.fileobj.seek(self.offset)
1714*cda5da8dSAndroid Build Coastguard Worker                    try:
1715*cda5da8dSAndroid Build Coastguard Worker                        tarinfo = self.tarinfo.fromtarfile(self)
1716*cda5da8dSAndroid Build Coastguard Worker                        self.members.append(tarinfo)
1717*cda5da8dSAndroid Build Coastguard Worker                    except EOFHeaderError:
1718*cda5da8dSAndroid Build Coastguard Worker                        self.fileobj.seek(self.offset)
1719*cda5da8dSAndroid Build Coastguard Worker                        break
1720*cda5da8dSAndroid Build Coastguard Worker                    except HeaderError as e:
1721*cda5da8dSAndroid Build Coastguard Worker                        raise ReadError(str(e)) from None
1722*cda5da8dSAndroid Build Coastguard Worker
1723*cda5da8dSAndroid Build Coastguard Worker            if self.mode in ("a", "w", "x"):
1724*cda5da8dSAndroid Build Coastguard Worker                self._loaded = True
1725*cda5da8dSAndroid Build Coastguard Worker
1726*cda5da8dSAndroid Build Coastguard Worker                if self.pax_headers:
1727*cda5da8dSAndroid Build Coastguard Worker                    buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy())
1728*cda5da8dSAndroid Build Coastguard Worker                    self.fileobj.write(buf)
1729*cda5da8dSAndroid Build Coastguard Worker                    self.offset += len(buf)
1730*cda5da8dSAndroid Build Coastguard Worker        except:
1731*cda5da8dSAndroid Build Coastguard Worker            if not self._extfileobj:
1732*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.close()
1733*cda5da8dSAndroid Build Coastguard Worker            self.closed = True
1734*cda5da8dSAndroid Build Coastguard Worker            raise
1735*cda5da8dSAndroid Build Coastguard Worker
1736*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
1737*cda5da8dSAndroid Build Coastguard Worker    # Below are the classmethods which act as alternate constructors to the
1738*cda5da8dSAndroid Build Coastguard Worker    # TarFile class. The open() method is the only one that is needed for
1739*cda5da8dSAndroid Build Coastguard Worker    # public use; it is the "super"-constructor and is able to select an
1740*cda5da8dSAndroid Build Coastguard Worker    # adequate "sub"-constructor for a particular compression using the mapping
1741*cda5da8dSAndroid Build Coastguard Worker    # from OPEN_METH.
1742*cda5da8dSAndroid Build Coastguard Worker    #
1743*cda5da8dSAndroid Build Coastguard Worker    # This concept allows one to subclass TarFile without losing the comfort of
1744*cda5da8dSAndroid Build Coastguard Worker    # the super-constructor. A sub-constructor is registered and made available
1745*cda5da8dSAndroid Build Coastguard Worker    # by adding it to the mapping in OPEN_METH.
1746*cda5da8dSAndroid Build Coastguard Worker
1747*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1748*cda5da8dSAndroid Build Coastguard Worker    def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs):
1749*cda5da8dSAndroid Build Coastguard Worker        """Open a tar archive for reading, writing or appending. Return
1750*cda5da8dSAndroid Build Coastguard Worker           an appropriate TarFile class.
1751*cda5da8dSAndroid Build Coastguard Worker
1752*cda5da8dSAndroid Build Coastguard Worker           mode:
1753*cda5da8dSAndroid Build Coastguard Worker           'r' or 'r:*' open for reading with transparent compression
1754*cda5da8dSAndroid Build Coastguard Worker           'r:'         open for reading exclusively uncompressed
1755*cda5da8dSAndroid Build Coastguard Worker           'r:gz'       open for reading with gzip compression
1756*cda5da8dSAndroid Build Coastguard Worker           'r:bz2'      open for reading with bzip2 compression
1757*cda5da8dSAndroid Build Coastguard Worker           'r:xz'       open for reading with lzma compression
1758*cda5da8dSAndroid Build Coastguard Worker           'a' or 'a:'  open for appending, creating the file if necessary
1759*cda5da8dSAndroid Build Coastguard Worker           'w' or 'w:'  open for writing without compression
1760*cda5da8dSAndroid Build Coastguard Worker           'w:gz'       open for writing with gzip compression
1761*cda5da8dSAndroid Build Coastguard Worker           'w:bz2'      open for writing with bzip2 compression
1762*cda5da8dSAndroid Build Coastguard Worker           'w:xz'       open for writing with lzma compression
1763*cda5da8dSAndroid Build Coastguard Worker
1764*cda5da8dSAndroid Build Coastguard Worker           'x' or 'x:'  create a tarfile exclusively without compression, raise
1765*cda5da8dSAndroid Build Coastguard Worker                        an exception if the file is already created
1766*cda5da8dSAndroid Build Coastguard Worker           'x:gz'       create a gzip compressed tarfile, raise an exception
1767*cda5da8dSAndroid Build Coastguard Worker                        if the file is already created
1768*cda5da8dSAndroid Build Coastguard Worker           'x:bz2'      create a bzip2 compressed tarfile, raise an exception
1769*cda5da8dSAndroid Build Coastguard Worker                        if the file is already created
1770*cda5da8dSAndroid Build Coastguard Worker           'x:xz'       create an lzma compressed tarfile, raise an exception
1771*cda5da8dSAndroid Build Coastguard Worker                        if the file is already created
1772*cda5da8dSAndroid Build Coastguard Worker
1773*cda5da8dSAndroid Build Coastguard Worker           'r|*'        open a stream of tar blocks with transparent compression
1774*cda5da8dSAndroid Build Coastguard Worker           'r|'         open an uncompressed stream of tar blocks for reading
1775*cda5da8dSAndroid Build Coastguard Worker           'r|gz'       open a gzip compressed stream of tar blocks
1776*cda5da8dSAndroid Build Coastguard Worker           'r|bz2'      open a bzip2 compressed stream of tar blocks
1777*cda5da8dSAndroid Build Coastguard Worker           'r|xz'       open an lzma compressed stream of tar blocks
1778*cda5da8dSAndroid Build Coastguard Worker           'w|'         open an uncompressed stream for writing
1779*cda5da8dSAndroid Build Coastguard Worker           'w|gz'       open a gzip compressed stream for writing
1780*cda5da8dSAndroid Build Coastguard Worker           'w|bz2'      open a bzip2 compressed stream for writing
1781*cda5da8dSAndroid Build Coastguard Worker           'w|xz'       open an lzma compressed stream for writing
1782*cda5da8dSAndroid Build Coastguard Worker        """
1783*cda5da8dSAndroid Build Coastguard Worker
1784*cda5da8dSAndroid Build Coastguard Worker        if not name and not fileobj:
1785*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("nothing to open")
1786*cda5da8dSAndroid Build Coastguard Worker
1787*cda5da8dSAndroid Build Coastguard Worker        if mode in ("r", "r:*"):
1788*cda5da8dSAndroid Build Coastguard Worker            # Find out which *open() is appropriate for opening the file.
1789*cda5da8dSAndroid Build Coastguard Worker            def not_compressed(comptype):
1790*cda5da8dSAndroid Build Coastguard Worker                return cls.OPEN_METH[comptype] == 'taropen'
1791*cda5da8dSAndroid Build Coastguard Worker            error_msgs = []
1792*cda5da8dSAndroid Build Coastguard Worker            for comptype in sorted(cls.OPEN_METH, key=not_compressed):
1793*cda5da8dSAndroid Build Coastguard Worker                func = getattr(cls, cls.OPEN_METH[comptype])
1794*cda5da8dSAndroid Build Coastguard Worker                if fileobj is not None:
1795*cda5da8dSAndroid Build Coastguard Worker                    saved_pos = fileobj.tell()
1796*cda5da8dSAndroid Build Coastguard Worker                try:
1797*cda5da8dSAndroid Build Coastguard Worker                    return func(name, "r", fileobj, **kwargs)
1798*cda5da8dSAndroid Build Coastguard Worker                except (ReadError, CompressionError) as e:
1799*cda5da8dSAndroid Build Coastguard Worker                    error_msgs.append(f'- method {comptype}: {e!r}')
1800*cda5da8dSAndroid Build Coastguard Worker                    if fileobj is not None:
1801*cda5da8dSAndroid Build Coastguard Worker                        fileobj.seek(saved_pos)
1802*cda5da8dSAndroid Build Coastguard Worker                    continue
1803*cda5da8dSAndroid Build Coastguard Worker            error_msgs_summary = '\n'.join(error_msgs)
1804*cda5da8dSAndroid Build Coastguard Worker            raise ReadError(f"file could not be opened successfully:\n{error_msgs_summary}")
1805*cda5da8dSAndroid Build Coastguard Worker
1806*cda5da8dSAndroid Build Coastguard Worker        elif ":" in mode:
1807*cda5da8dSAndroid Build Coastguard Worker            filemode, comptype = mode.split(":", 1)
1808*cda5da8dSAndroid Build Coastguard Worker            filemode = filemode or "r"
1809*cda5da8dSAndroid Build Coastguard Worker            comptype = comptype or "tar"
1810*cda5da8dSAndroid Build Coastguard Worker
1811*cda5da8dSAndroid Build Coastguard Worker            # Select the *open() function according to
1812*cda5da8dSAndroid Build Coastguard Worker            # given compression.
1813*cda5da8dSAndroid Build Coastguard Worker            if comptype in cls.OPEN_METH:
1814*cda5da8dSAndroid Build Coastguard Worker                func = getattr(cls, cls.OPEN_METH[comptype])
1815*cda5da8dSAndroid Build Coastguard Worker            else:
1816*cda5da8dSAndroid Build Coastguard Worker                raise CompressionError("unknown compression type %r" % comptype)
1817*cda5da8dSAndroid Build Coastguard Worker            return func(name, filemode, fileobj, **kwargs)
1818*cda5da8dSAndroid Build Coastguard Worker
1819*cda5da8dSAndroid Build Coastguard Worker        elif "|" in mode:
1820*cda5da8dSAndroid Build Coastguard Worker            filemode, comptype = mode.split("|", 1)
1821*cda5da8dSAndroid Build Coastguard Worker            filemode = filemode or "r"
1822*cda5da8dSAndroid Build Coastguard Worker            comptype = comptype or "tar"
1823*cda5da8dSAndroid Build Coastguard Worker
1824*cda5da8dSAndroid Build Coastguard Worker            if filemode not in ("r", "w"):
1825*cda5da8dSAndroid Build Coastguard Worker                raise ValueError("mode must be 'r' or 'w'")
1826*cda5da8dSAndroid Build Coastguard Worker
1827*cda5da8dSAndroid Build Coastguard Worker            stream = _Stream(name, filemode, comptype, fileobj, bufsize)
1828*cda5da8dSAndroid Build Coastguard Worker            try:
1829*cda5da8dSAndroid Build Coastguard Worker                t = cls(name, filemode, stream, **kwargs)
1830*cda5da8dSAndroid Build Coastguard Worker            except:
1831*cda5da8dSAndroid Build Coastguard Worker                stream.close()
1832*cda5da8dSAndroid Build Coastguard Worker                raise
1833*cda5da8dSAndroid Build Coastguard Worker            t._extfileobj = False
1834*cda5da8dSAndroid Build Coastguard Worker            return t
1835*cda5da8dSAndroid Build Coastguard Worker
1836*cda5da8dSAndroid Build Coastguard Worker        elif mode in ("a", "w", "x"):
1837*cda5da8dSAndroid Build Coastguard Worker            return cls.taropen(name, mode, fileobj, **kwargs)
1838*cda5da8dSAndroid Build Coastguard Worker
1839*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("undiscernible mode")
1840*cda5da8dSAndroid Build Coastguard Worker
1841*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1842*cda5da8dSAndroid Build Coastguard Worker    def taropen(cls, name, mode="r", fileobj=None, **kwargs):
1843*cda5da8dSAndroid Build Coastguard Worker        """Open uncompressed tar archive name for reading or writing.
1844*cda5da8dSAndroid Build Coastguard Worker        """
1845*cda5da8dSAndroid Build Coastguard Worker        if mode not in ("r", "a", "w", "x"):
1846*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
1847*cda5da8dSAndroid Build Coastguard Worker        return cls(name, mode, fileobj, **kwargs)
1848*cda5da8dSAndroid Build Coastguard Worker
1849*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1850*cda5da8dSAndroid Build Coastguard Worker    def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
1851*cda5da8dSAndroid Build Coastguard Worker        """Open gzip compressed tar archive name for reading or writing.
1852*cda5da8dSAndroid Build Coastguard Worker           Appending is not allowed.
1853*cda5da8dSAndroid Build Coastguard Worker        """
1854*cda5da8dSAndroid Build Coastguard Worker        if mode not in ("r", "w", "x"):
1855*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("mode must be 'r', 'w' or 'x'")
1856*cda5da8dSAndroid Build Coastguard Worker
1857*cda5da8dSAndroid Build Coastguard Worker        try:
1858*cda5da8dSAndroid Build Coastguard Worker            from gzip import GzipFile
1859*cda5da8dSAndroid Build Coastguard Worker        except ImportError:
1860*cda5da8dSAndroid Build Coastguard Worker            raise CompressionError("gzip module is not available") from None
1861*cda5da8dSAndroid Build Coastguard Worker
1862*cda5da8dSAndroid Build Coastguard Worker        try:
1863*cda5da8dSAndroid Build Coastguard Worker            fileobj = GzipFile(name, mode + "b", compresslevel, fileobj)
1864*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
1865*cda5da8dSAndroid Build Coastguard Worker            if fileobj is not None and mode == 'r':
1866*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("not a gzip file") from e
1867*cda5da8dSAndroid Build Coastguard Worker            raise
1868*cda5da8dSAndroid Build Coastguard Worker
1869*cda5da8dSAndroid Build Coastguard Worker        try:
1870*cda5da8dSAndroid Build Coastguard Worker            t = cls.taropen(name, mode, fileobj, **kwargs)
1871*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
1872*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1873*cda5da8dSAndroid Build Coastguard Worker            if mode == 'r':
1874*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("not a gzip file") from e
1875*cda5da8dSAndroid Build Coastguard Worker            raise
1876*cda5da8dSAndroid Build Coastguard Worker        except:
1877*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1878*cda5da8dSAndroid Build Coastguard Worker            raise
1879*cda5da8dSAndroid Build Coastguard Worker        t._extfileobj = False
1880*cda5da8dSAndroid Build Coastguard Worker        return t
1881*cda5da8dSAndroid Build Coastguard Worker
1882*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1883*cda5da8dSAndroid Build Coastguard Worker    def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
1884*cda5da8dSAndroid Build Coastguard Worker        """Open bzip2 compressed tar archive name for reading or writing.
1885*cda5da8dSAndroid Build Coastguard Worker           Appending is not allowed.
1886*cda5da8dSAndroid Build Coastguard Worker        """
1887*cda5da8dSAndroid Build Coastguard Worker        if mode not in ("r", "w", "x"):
1888*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("mode must be 'r', 'w' or 'x'")
1889*cda5da8dSAndroid Build Coastguard Worker
1890*cda5da8dSAndroid Build Coastguard Worker        try:
1891*cda5da8dSAndroid Build Coastguard Worker            from bz2 import BZ2File
1892*cda5da8dSAndroid Build Coastguard Worker        except ImportError:
1893*cda5da8dSAndroid Build Coastguard Worker            raise CompressionError("bz2 module is not available") from None
1894*cda5da8dSAndroid Build Coastguard Worker
1895*cda5da8dSAndroid Build Coastguard Worker        fileobj = BZ2File(fileobj or name, mode, compresslevel=compresslevel)
1896*cda5da8dSAndroid Build Coastguard Worker
1897*cda5da8dSAndroid Build Coastguard Worker        try:
1898*cda5da8dSAndroid Build Coastguard Worker            t = cls.taropen(name, mode, fileobj, **kwargs)
1899*cda5da8dSAndroid Build Coastguard Worker        except (OSError, EOFError) as e:
1900*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1901*cda5da8dSAndroid Build Coastguard Worker            if mode == 'r':
1902*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("not a bzip2 file") from e
1903*cda5da8dSAndroid Build Coastguard Worker            raise
1904*cda5da8dSAndroid Build Coastguard Worker        except:
1905*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1906*cda5da8dSAndroid Build Coastguard Worker            raise
1907*cda5da8dSAndroid Build Coastguard Worker        t._extfileobj = False
1908*cda5da8dSAndroid Build Coastguard Worker        return t
1909*cda5da8dSAndroid Build Coastguard Worker
1910*cda5da8dSAndroid Build Coastguard Worker    @classmethod
1911*cda5da8dSAndroid Build Coastguard Worker    def xzopen(cls, name, mode="r", fileobj=None, preset=None, **kwargs):
1912*cda5da8dSAndroid Build Coastguard Worker        """Open lzma compressed tar archive name for reading or writing.
1913*cda5da8dSAndroid Build Coastguard Worker           Appending is not allowed.
1914*cda5da8dSAndroid Build Coastguard Worker        """
1915*cda5da8dSAndroid Build Coastguard Worker        if mode not in ("r", "w", "x"):
1916*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("mode must be 'r', 'w' or 'x'")
1917*cda5da8dSAndroid Build Coastguard Worker
1918*cda5da8dSAndroid Build Coastguard Worker        try:
1919*cda5da8dSAndroid Build Coastguard Worker            from lzma import LZMAFile, LZMAError
1920*cda5da8dSAndroid Build Coastguard Worker        except ImportError:
1921*cda5da8dSAndroid Build Coastguard Worker            raise CompressionError("lzma module is not available") from None
1922*cda5da8dSAndroid Build Coastguard Worker
1923*cda5da8dSAndroid Build Coastguard Worker        fileobj = LZMAFile(fileobj or name, mode, preset=preset)
1924*cda5da8dSAndroid Build Coastguard Worker
1925*cda5da8dSAndroid Build Coastguard Worker        try:
1926*cda5da8dSAndroid Build Coastguard Worker            t = cls.taropen(name, mode, fileobj, **kwargs)
1927*cda5da8dSAndroid Build Coastguard Worker        except (LZMAError, EOFError) as e:
1928*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1929*cda5da8dSAndroid Build Coastguard Worker            if mode == 'r':
1930*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("not an lzma file") from e
1931*cda5da8dSAndroid Build Coastguard Worker            raise
1932*cda5da8dSAndroid Build Coastguard Worker        except:
1933*cda5da8dSAndroid Build Coastguard Worker            fileobj.close()
1934*cda5da8dSAndroid Build Coastguard Worker            raise
1935*cda5da8dSAndroid Build Coastguard Worker        t._extfileobj = False
1936*cda5da8dSAndroid Build Coastguard Worker        return t
1937*cda5da8dSAndroid Build Coastguard Worker
1938*cda5da8dSAndroid Build Coastguard Worker    # All *open() methods are registered here.
1939*cda5da8dSAndroid Build Coastguard Worker    OPEN_METH = {
1940*cda5da8dSAndroid Build Coastguard Worker        "tar": "taropen",   # uncompressed tar
1941*cda5da8dSAndroid Build Coastguard Worker        "gz":  "gzopen",    # gzip compressed tar
1942*cda5da8dSAndroid Build Coastguard Worker        "bz2": "bz2open",   # bzip2 compressed tar
1943*cda5da8dSAndroid Build Coastguard Worker        "xz":  "xzopen"     # lzma compressed tar
1944*cda5da8dSAndroid Build Coastguard Worker    }
1945*cda5da8dSAndroid Build Coastguard Worker
1946*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
1947*cda5da8dSAndroid Build Coastguard Worker    # The public methods which TarFile provides:
1948*cda5da8dSAndroid Build Coastguard Worker
1949*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1950*cda5da8dSAndroid Build Coastguard Worker        """Close the TarFile. In write-mode, two finishing zero blocks are
1951*cda5da8dSAndroid Build Coastguard Worker           appended to the archive.
1952*cda5da8dSAndroid Build Coastguard Worker        """
1953*cda5da8dSAndroid Build Coastguard Worker        if self.closed:
1954*cda5da8dSAndroid Build Coastguard Worker            return
1955*cda5da8dSAndroid Build Coastguard Worker
1956*cda5da8dSAndroid Build Coastguard Worker        self.closed = True
1957*cda5da8dSAndroid Build Coastguard Worker        try:
1958*cda5da8dSAndroid Build Coastguard Worker            if self.mode in ("a", "w", "x"):
1959*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.write(NUL * (BLOCKSIZE * 2))
1960*cda5da8dSAndroid Build Coastguard Worker                self.offset += (BLOCKSIZE * 2)
1961*cda5da8dSAndroid Build Coastguard Worker                # fill up the end with zero-blocks
1962*cda5da8dSAndroid Build Coastguard Worker                # (like option -b20 for tar does)
1963*cda5da8dSAndroid Build Coastguard Worker                blocks, remainder = divmod(self.offset, RECORDSIZE)
1964*cda5da8dSAndroid Build Coastguard Worker                if remainder > 0:
1965*cda5da8dSAndroid Build Coastguard Worker                    self.fileobj.write(NUL * (RECORDSIZE - remainder))
1966*cda5da8dSAndroid Build Coastguard Worker        finally:
1967*cda5da8dSAndroid Build Coastguard Worker            if not self._extfileobj:
1968*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.close()
1969*cda5da8dSAndroid Build Coastguard Worker
1970*cda5da8dSAndroid Build Coastguard Worker    def getmember(self, name):
1971*cda5da8dSAndroid Build Coastguard Worker        """Return a TarInfo object for member `name'. If `name' can not be
1972*cda5da8dSAndroid Build Coastguard Worker           found in the archive, KeyError is raised. If a member occurs more
1973*cda5da8dSAndroid Build Coastguard Worker           than once in the archive, its last occurrence is assumed to be the
1974*cda5da8dSAndroid Build Coastguard Worker           most up-to-date version.
1975*cda5da8dSAndroid Build Coastguard Worker        """
1976*cda5da8dSAndroid Build Coastguard Worker        tarinfo = self._getmember(name.rstrip('/'))
1977*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is None:
1978*cda5da8dSAndroid Build Coastguard Worker            raise KeyError("filename %r not found" % name)
1979*cda5da8dSAndroid Build Coastguard Worker        return tarinfo
1980*cda5da8dSAndroid Build Coastguard Worker
1981*cda5da8dSAndroid Build Coastguard Worker    def getmembers(self):
1982*cda5da8dSAndroid Build Coastguard Worker        """Return the members of the archive as a list of TarInfo objects. The
1983*cda5da8dSAndroid Build Coastguard Worker           list has the same order as the members in the archive.
1984*cda5da8dSAndroid Build Coastguard Worker        """
1985*cda5da8dSAndroid Build Coastguard Worker        self._check()
1986*cda5da8dSAndroid Build Coastguard Worker        if not self._loaded:    # if we want to obtain a list of
1987*cda5da8dSAndroid Build Coastguard Worker            self._load()        # all members, we first have to
1988*cda5da8dSAndroid Build Coastguard Worker                                # scan the whole archive.
1989*cda5da8dSAndroid Build Coastguard Worker        return self.members
1990*cda5da8dSAndroid Build Coastguard Worker
1991*cda5da8dSAndroid Build Coastguard Worker    def getnames(self):
1992*cda5da8dSAndroid Build Coastguard Worker        """Return the members of the archive as a list of their names. It has
1993*cda5da8dSAndroid Build Coastguard Worker           the same order as the list returned by getmembers().
1994*cda5da8dSAndroid Build Coastguard Worker        """
1995*cda5da8dSAndroid Build Coastguard Worker        return [tarinfo.name for tarinfo in self.getmembers()]
1996*cda5da8dSAndroid Build Coastguard Worker
1997*cda5da8dSAndroid Build Coastguard Worker    def gettarinfo(self, name=None, arcname=None, fileobj=None):
1998*cda5da8dSAndroid Build Coastguard Worker        """Create a TarInfo object from the result of os.stat or equivalent
1999*cda5da8dSAndroid Build Coastguard Worker           on an existing file. The file is either named by `name', or
2000*cda5da8dSAndroid Build Coastguard Worker           specified as a file object `fileobj' with a file descriptor. If
2001*cda5da8dSAndroid Build Coastguard Worker           given, `arcname' specifies an alternative name for the file in the
2002*cda5da8dSAndroid Build Coastguard Worker           archive, otherwise, the name is taken from the 'name' attribute of
2003*cda5da8dSAndroid Build Coastguard Worker           'fileobj', or the 'name' argument. The name should be a text
2004*cda5da8dSAndroid Build Coastguard Worker           string.
2005*cda5da8dSAndroid Build Coastguard Worker        """
2006*cda5da8dSAndroid Build Coastguard Worker        self._check("awx")
2007*cda5da8dSAndroid Build Coastguard Worker
2008*cda5da8dSAndroid Build Coastguard Worker        # When fileobj is given, replace name by
2009*cda5da8dSAndroid Build Coastguard Worker        # fileobj's real name.
2010*cda5da8dSAndroid Build Coastguard Worker        if fileobj is not None:
2011*cda5da8dSAndroid Build Coastguard Worker            name = fileobj.name
2012*cda5da8dSAndroid Build Coastguard Worker
2013*cda5da8dSAndroid Build Coastguard Worker        # Building the name of the member in the archive.
2014*cda5da8dSAndroid Build Coastguard Worker        # Backward slashes are converted to forward slashes,
2015*cda5da8dSAndroid Build Coastguard Worker        # Absolute paths are turned to relative paths.
2016*cda5da8dSAndroid Build Coastguard Worker        if arcname is None:
2017*cda5da8dSAndroid Build Coastguard Worker            arcname = name
2018*cda5da8dSAndroid Build Coastguard Worker        drv, arcname = os.path.splitdrive(arcname)
2019*cda5da8dSAndroid Build Coastguard Worker        arcname = arcname.replace(os.sep, "/")
2020*cda5da8dSAndroid Build Coastguard Worker        arcname = arcname.lstrip("/")
2021*cda5da8dSAndroid Build Coastguard Worker
2022*cda5da8dSAndroid Build Coastguard Worker        # Now, fill the TarInfo object with
2023*cda5da8dSAndroid Build Coastguard Worker        # information specific for the file.
2024*cda5da8dSAndroid Build Coastguard Worker        tarinfo = self.tarinfo()
2025*cda5da8dSAndroid Build Coastguard Worker        tarinfo.tarfile = self  # Not needed
2026*cda5da8dSAndroid Build Coastguard Worker
2027*cda5da8dSAndroid Build Coastguard Worker        # Use os.stat or os.lstat, depending on if symlinks shall be resolved.
2028*cda5da8dSAndroid Build Coastguard Worker        if fileobj is None:
2029*cda5da8dSAndroid Build Coastguard Worker            if not self.dereference:
2030*cda5da8dSAndroid Build Coastguard Worker                statres = os.lstat(name)
2031*cda5da8dSAndroid Build Coastguard Worker            else:
2032*cda5da8dSAndroid Build Coastguard Worker                statres = os.stat(name)
2033*cda5da8dSAndroid Build Coastguard Worker        else:
2034*cda5da8dSAndroid Build Coastguard Worker            statres = os.fstat(fileobj.fileno())
2035*cda5da8dSAndroid Build Coastguard Worker        linkname = ""
2036*cda5da8dSAndroid Build Coastguard Worker
2037*cda5da8dSAndroid Build Coastguard Worker        stmd = statres.st_mode
2038*cda5da8dSAndroid Build Coastguard Worker        if stat.S_ISREG(stmd):
2039*cda5da8dSAndroid Build Coastguard Worker            inode = (statres.st_ino, statres.st_dev)
2040*cda5da8dSAndroid Build Coastguard Worker            if not self.dereference and statres.st_nlink > 1 and \
2041*cda5da8dSAndroid Build Coastguard Worker                    inode in self.inodes and arcname != self.inodes[inode]:
2042*cda5da8dSAndroid Build Coastguard Worker                # Is it a hardlink to an already
2043*cda5da8dSAndroid Build Coastguard Worker                # archived file?
2044*cda5da8dSAndroid Build Coastguard Worker                type = LNKTYPE
2045*cda5da8dSAndroid Build Coastguard Worker                linkname = self.inodes[inode]
2046*cda5da8dSAndroid Build Coastguard Worker            else:
2047*cda5da8dSAndroid Build Coastguard Worker                # The inode is added only if its valid.
2048*cda5da8dSAndroid Build Coastguard Worker                # For win32 it is always 0.
2049*cda5da8dSAndroid Build Coastguard Worker                type = REGTYPE
2050*cda5da8dSAndroid Build Coastguard Worker                if inode[0]:
2051*cda5da8dSAndroid Build Coastguard Worker                    self.inodes[inode] = arcname
2052*cda5da8dSAndroid Build Coastguard Worker        elif stat.S_ISDIR(stmd):
2053*cda5da8dSAndroid Build Coastguard Worker            type = DIRTYPE
2054*cda5da8dSAndroid Build Coastguard Worker        elif stat.S_ISFIFO(stmd):
2055*cda5da8dSAndroid Build Coastguard Worker            type = FIFOTYPE
2056*cda5da8dSAndroid Build Coastguard Worker        elif stat.S_ISLNK(stmd):
2057*cda5da8dSAndroid Build Coastguard Worker            type = SYMTYPE
2058*cda5da8dSAndroid Build Coastguard Worker            linkname = os.readlink(name)
2059*cda5da8dSAndroid Build Coastguard Worker        elif stat.S_ISCHR(stmd):
2060*cda5da8dSAndroid Build Coastguard Worker            type = CHRTYPE
2061*cda5da8dSAndroid Build Coastguard Worker        elif stat.S_ISBLK(stmd):
2062*cda5da8dSAndroid Build Coastguard Worker            type = BLKTYPE
2063*cda5da8dSAndroid Build Coastguard Worker        else:
2064*cda5da8dSAndroid Build Coastguard Worker            return None
2065*cda5da8dSAndroid Build Coastguard Worker
2066*cda5da8dSAndroid Build Coastguard Worker        # Fill the TarInfo object with all
2067*cda5da8dSAndroid Build Coastguard Worker        # information we can get.
2068*cda5da8dSAndroid Build Coastguard Worker        tarinfo.name = arcname
2069*cda5da8dSAndroid Build Coastguard Worker        tarinfo.mode = stmd
2070*cda5da8dSAndroid Build Coastguard Worker        tarinfo.uid = statres.st_uid
2071*cda5da8dSAndroid Build Coastguard Worker        tarinfo.gid = statres.st_gid
2072*cda5da8dSAndroid Build Coastguard Worker        if type == REGTYPE:
2073*cda5da8dSAndroid Build Coastguard Worker            tarinfo.size = statres.st_size
2074*cda5da8dSAndroid Build Coastguard Worker        else:
2075*cda5da8dSAndroid Build Coastguard Worker            tarinfo.size = 0
2076*cda5da8dSAndroid Build Coastguard Worker        tarinfo.mtime = statres.st_mtime
2077*cda5da8dSAndroid Build Coastguard Worker        tarinfo.type = type
2078*cda5da8dSAndroid Build Coastguard Worker        tarinfo.linkname = linkname
2079*cda5da8dSAndroid Build Coastguard Worker        if pwd:
2080*cda5da8dSAndroid Build Coastguard Worker            try:
2081*cda5da8dSAndroid Build Coastguard Worker                tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0]
2082*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
2083*cda5da8dSAndroid Build Coastguard Worker                pass
2084*cda5da8dSAndroid Build Coastguard Worker        if grp:
2085*cda5da8dSAndroid Build Coastguard Worker            try:
2086*cda5da8dSAndroid Build Coastguard Worker                tarinfo.gname = grp.getgrgid(tarinfo.gid)[0]
2087*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
2088*cda5da8dSAndroid Build Coastguard Worker                pass
2089*cda5da8dSAndroid Build Coastguard Worker
2090*cda5da8dSAndroid Build Coastguard Worker        if type in (CHRTYPE, BLKTYPE):
2091*cda5da8dSAndroid Build Coastguard Worker            if hasattr(os, "major") and hasattr(os, "minor"):
2092*cda5da8dSAndroid Build Coastguard Worker                tarinfo.devmajor = os.major(statres.st_rdev)
2093*cda5da8dSAndroid Build Coastguard Worker                tarinfo.devminor = os.minor(statres.st_rdev)
2094*cda5da8dSAndroid Build Coastguard Worker        return tarinfo
2095*cda5da8dSAndroid Build Coastguard Worker
2096*cda5da8dSAndroid Build Coastguard Worker    def list(self, verbose=True, *, members=None):
2097*cda5da8dSAndroid Build Coastguard Worker        """Print a table of contents to sys.stdout. If `verbose' is False, only
2098*cda5da8dSAndroid Build Coastguard Worker           the names of the members are printed. If it is True, an `ls -l'-like
2099*cda5da8dSAndroid Build Coastguard Worker           output is produced. `members' is optional and must be a subset of the
2100*cda5da8dSAndroid Build Coastguard Worker           list returned by getmembers().
2101*cda5da8dSAndroid Build Coastguard Worker        """
2102*cda5da8dSAndroid Build Coastguard Worker        self._check()
2103*cda5da8dSAndroid Build Coastguard Worker
2104*cda5da8dSAndroid Build Coastguard Worker        if members is None:
2105*cda5da8dSAndroid Build Coastguard Worker            members = self
2106*cda5da8dSAndroid Build Coastguard Worker        for tarinfo in members:
2107*cda5da8dSAndroid Build Coastguard Worker            if verbose:
2108*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.mode is None:
2109*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("??????????")
2110*cda5da8dSAndroid Build Coastguard Worker                else:
2111*cda5da8dSAndroid Build Coastguard Worker                    _safe_print(stat.filemode(tarinfo.mode))
2112*cda5da8dSAndroid Build Coastguard Worker                _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid,
2113*cda5da8dSAndroid Build Coastguard Worker                                       tarinfo.gname or tarinfo.gid))
2114*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.ischr() or tarinfo.isblk():
2115*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("%10s" %
2116*cda5da8dSAndroid Build Coastguard Worker                            ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor)))
2117*cda5da8dSAndroid Build Coastguard Worker                else:
2118*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("%10d" % tarinfo.size)
2119*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.mtime is None:
2120*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("????-??-?? ??:??:??")
2121*cda5da8dSAndroid Build Coastguard Worker                else:
2122*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("%d-%02d-%02d %02d:%02d:%02d" \
2123*cda5da8dSAndroid Build Coastguard Worker                                % time.localtime(tarinfo.mtime)[:6])
2124*cda5da8dSAndroid Build Coastguard Worker
2125*cda5da8dSAndroid Build Coastguard Worker            _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else ""))
2126*cda5da8dSAndroid Build Coastguard Worker
2127*cda5da8dSAndroid Build Coastguard Worker            if verbose:
2128*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.issym():
2129*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("-> " + tarinfo.linkname)
2130*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.islnk():
2131*cda5da8dSAndroid Build Coastguard Worker                    _safe_print("link to " + tarinfo.linkname)
2132*cda5da8dSAndroid Build Coastguard Worker            print()
2133*cda5da8dSAndroid Build Coastguard Worker
2134*cda5da8dSAndroid Build Coastguard Worker    def add(self, name, arcname=None, recursive=True, *, filter=None):
2135*cda5da8dSAndroid Build Coastguard Worker        """Add the file `name' to the archive. `name' may be any type of file
2136*cda5da8dSAndroid Build Coastguard Worker           (directory, fifo, symbolic link, etc.). If given, `arcname'
2137*cda5da8dSAndroid Build Coastguard Worker           specifies an alternative name for the file in the archive.
2138*cda5da8dSAndroid Build Coastguard Worker           Directories are added recursively by default. This can be avoided by
2139*cda5da8dSAndroid Build Coastguard Worker           setting `recursive' to False. `filter' is a function
2140*cda5da8dSAndroid Build Coastguard Worker           that expects a TarInfo object argument and returns the changed
2141*cda5da8dSAndroid Build Coastguard Worker           TarInfo object, if it returns None the TarInfo object will be
2142*cda5da8dSAndroid Build Coastguard Worker           excluded from the archive.
2143*cda5da8dSAndroid Build Coastguard Worker        """
2144*cda5da8dSAndroid Build Coastguard Worker        self._check("awx")
2145*cda5da8dSAndroid Build Coastguard Worker
2146*cda5da8dSAndroid Build Coastguard Worker        if arcname is None:
2147*cda5da8dSAndroid Build Coastguard Worker            arcname = name
2148*cda5da8dSAndroid Build Coastguard Worker
2149*cda5da8dSAndroid Build Coastguard Worker        # Skip if somebody tries to archive the archive...
2150*cda5da8dSAndroid Build Coastguard Worker        if self.name is not None and os.path.abspath(name) == self.name:
2151*cda5da8dSAndroid Build Coastguard Worker            self._dbg(2, "tarfile: Skipped %r" % name)
2152*cda5da8dSAndroid Build Coastguard Worker            return
2153*cda5da8dSAndroid Build Coastguard Worker
2154*cda5da8dSAndroid Build Coastguard Worker        self._dbg(1, name)
2155*cda5da8dSAndroid Build Coastguard Worker
2156*cda5da8dSAndroid Build Coastguard Worker        # Create a TarInfo object from the file.
2157*cda5da8dSAndroid Build Coastguard Worker        tarinfo = self.gettarinfo(name, arcname)
2158*cda5da8dSAndroid Build Coastguard Worker
2159*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is None:
2160*cda5da8dSAndroid Build Coastguard Worker            self._dbg(1, "tarfile: Unsupported type %r" % name)
2161*cda5da8dSAndroid Build Coastguard Worker            return
2162*cda5da8dSAndroid Build Coastguard Worker
2163*cda5da8dSAndroid Build Coastguard Worker        # Change or exclude the TarInfo object.
2164*cda5da8dSAndroid Build Coastguard Worker        if filter is not None:
2165*cda5da8dSAndroid Build Coastguard Worker            tarinfo = filter(tarinfo)
2166*cda5da8dSAndroid Build Coastguard Worker            if tarinfo is None:
2167*cda5da8dSAndroid Build Coastguard Worker                self._dbg(2, "tarfile: Excluded %r" % name)
2168*cda5da8dSAndroid Build Coastguard Worker                return
2169*cda5da8dSAndroid Build Coastguard Worker
2170*cda5da8dSAndroid Build Coastguard Worker        # Append the tar header and data to the archive.
2171*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.isreg():
2172*cda5da8dSAndroid Build Coastguard Worker            with bltn_open(name, "rb") as f:
2173*cda5da8dSAndroid Build Coastguard Worker                self.addfile(tarinfo, f)
2174*cda5da8dSAndroid Build Coastguard Worker
2175*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.isdir():
2176*cda5da8dSAndroid Build Coastguard Worker            self.addfile(tarinfo)
2177*cda5da8dSAndroid Build Coastguard Worker            if recursive:
2178*cda5da8dSAndroid Build Coastguard Worker                for f in sorted(os.listdir(name)):
2179*cda5da8dSAndroid Build Coastguard Worker                    self.add(os.path.join(name, f), os.path.join(arcname, f),
2180*cda5da8dSAndroid Build Coastguard Worker                            recursive, filter=filter)
2181*cda5da8dSAndroid Build Coastguard Worker
2182*cda5da8dSAndroid Build Coastguard Worker        else:
2183*cda5da8dSAndroid Build Coastguard Worker            self.addfile(tarinfo)
2184*cda5da8dSAndroid Build Coastguard Worker
2185*cda5da8dSAndroid Build Coastguard Worker    def addfile(self, tarinfo, fileobj=None):
2186*cda5da8dSAndroid Build Coastguard Worker        """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is
2187*cda5da8dSAndroid Build Coastguard Worker           given, it should be a binary file, and tarinfo.size bytes are read
2188*cda5da8dSAndroid Build Coastguard Worker           from it and added to the archive. You can create TarInfo objects
2189*cda5da8dSAndroid Build Coastguard Worker           directly, or by using gettarinfo().
2190*cda5da8dSAndroid Build Coastguard Worker        """
2191*cda5da8dSAndroid Build Coastguard Worker        self._check("awx")
2192*cda5da8dSAndroid Build Coastguard Worker
2193*cda5da8dSAndroid Build Coastguard Worker        tarinfo = copy.copy(tarinfo)
2194*cda5da8dSAndroid Build Coastguard Worker
2195*cda5da8dSAndroid Build Coastguard Worker        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
2196*cda5da8dSAndroid Build Coastguard Worker        self.fileobj.write(buf)
2197*cda5da8dSAndroid Build Coastguard Worker        self.offset += len(buf)
2198*cda5da8dSAndroid Build Coastguard Worker        bufsize=self.copybufsize
2199*cda5da8dSAndroid Build Coastguard Worker        # If there's data to follow, append it.
2200*cda5da8dSAndroid Build Coastguard Worker        if fileobj is not None:
2201*cda5da8dSAndroid Build Coastguard Worker            copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize)
2202*cda5da8dSAndroid Build Coastguard Worker            blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
2203*cda5da8dSAndroid Build Coastguard Worker            if remainder > 0:
2204*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.write(NUL * (BLOCKSIZE - remainder))
2205*cda5da8dSAndroid Build Coastguard Worker                blocks += 1
2206*cda5da8dSAndroid Build Coastguard Worker            self.offset += blocks * BLOCKSIZE
2207*cda5da8dSAndroid Build Coastguard Worker
2208*cda5da8dSAndroid Build Coastguard Worker        self.members.append(tarinfo)
2209*cda5da8dSAndroid Build Coastguard Worker
2210*cda5da8dSAndroid Build Coastguard Worker    def _get_filter_function(self, filter):
2211*cda5da8dSAndroid Build Coastguard Worker        if filter is None:
2212*cda5da8dSAndroid Build Coastguard Worker            filter = self.extraction_filter
2213*cda5da8dSAndroid Build Coastguard Worker            if filter is None:
2214*cda5da8dSAndroid Build Coastguard Worker                return fully_trusted_filter
2215*cda5da8dSAndroid Build Coastguard Worker            if isinstance(filter, str):
2216*cda5da8dSAndroid Build Coastguard Worker                raise TypeError(
2217*cda5da8dSAndroid Build Coastguard Worker                    'String names are not supported for '
2218*cda5da8dSAndroid Build Coastguard Worker                    + 'TarFile.extraction_filter. Use a function such as '
2219*cda5da8dSAndroid Build Coastguard Worker                    + 'tarfile.data_filter directly.')
2220*cda5da8dSAndroid Build Coastguard Worker            return filter
2221*cda5da8dSAndroid Build Coastguard Worker        if callable(filter):
2222*cda5da8dSAndroid Build Coastguard Worker            return filter
2223*cda5da8dSAndroid Build Coastguard Worker        try:
2224*cda5da8dSAndroid Build Coastguard Worker            return _NAMED_FILTERS[filter]
2225*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
2226*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(f"filter {filter!r} not found") from None
2227*cda5da8dSAndroid Build Coastguard Worker
2228*cda5da8dSAndroid Build Coastguard Worker    def extractall(self, path=".", members=None, *, numeric_owner=False,
2229*cda5da8dSAndroid Build Coastguard Worker                   filter=None):
2230*cda5da8dSAndroid Build Coastguard Worker        """Extract all members from the archive to the current working
2231*cda5da8dSAndroid Build Coastguard Worker           directory and set owner, modification time and permissions on
2232*cda5da8dSAndroid Build Coastguard Worker           directories afterwards. `path' specifies a different directory
2233*cda5da8dSAndroid Build Coastguard Worker           to extract to. `members' is optional and must be a subset of the
2234*cda5da8dSAndroid Build Coastguard Worker           list returned by getmembers(). If `numeric_owner` is True, only
2235*cda5da8dSAndroid Build Coastguard Worker           the numbers for user/group names are used and not the names.
2236*cda5da8dSAndroid Build Coastguard Worker
2237*cda5da8dSAndroid Build Coastguard Worker           The `filter` function will be called on each member just
2238*cda5da8dSAndroid Build Coastguard Worker           before extraction.
2239*cda5da8dSAndroid Build Coastguard Worker           It can return a changed TarInfo or None to skip the member.
2240*cda5da8dSAndroid Build Coastguard Worker           String names of common filters are accepted.
2241*cda5da8dSAndroid Build Coastguard Worker        """
2242*cda5da8dSAndroid Build Coastguard Worker        directories = []
2243*cda5da8dSAndroid Build Coastguard Worker
2244*cda5da8dSAndroid Build Coastguard Worker        filter_function = self._get_filter_function(filter)
2245*cda5da8dSAndroid Build Coastguard Worker        if members is None:
2246*cda5da8dSAndroid Build Coastguard Worker            members = self
2247*cda5da8dSAndroid Build Coastguard Worker
2248*cda5da8dSAndroid Build Coastguard Worker        for member in members:
2249*cda5da8dSAndroid Build Coastguard Worker            tarinfo = self._get_extract_tarinfo(member, filter_function, path)
2250*cda5da8dSAndroid Build Coastguard Worker            if tarinfo is None:
2251*cda5da8dSAndroid Build Coastguard Worker                continue
2252*cda5da8dSAndroid Build Coastguard Worker            if tarinfo.isdir():
2253*cda5da8dSAndroid Build Coastguard Worker                # For directories, delay setting attributes until later,
2254*cda5da8dSAndroid Build Coastguard Worker                # since permissions can interfere with extraction and
2255*cda5da8dSAndroid Build Coastguard Worker                # extracting contents can reset mtime.
2256*cda5da8dSAndroid Build Coastguard Worker                directories.append(tarinfo)
2257*cda5da8dSAndroid Build Coastguard Worker            self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(),
2258*cda5da8dSAndroid Build Coastguard Worker                              numeric_owner=numeric_owner)
2259*cda5da8dSAndroid Build Coastguard Worker
2260*cda5da8dSAndroid Build Coastguard Worker        # Reverse sort directories.
2261*cda5da8dSAndroid Build Coastguard Worker        directories.sort(key=lambda a: a.name, reverse=True)
2262*cda5da8dSAndroid Build Coastguard Worker
2263*cda5da8dSAndroid Build Coastguard Worker        # Set correct owner, mtime and filemode on directories.
2264*cda5da8dSAndroid Build Coastguard Worker        for tarinfo in directories:
2265*cda5da8dSAndroid Build Coastguard Worker            dirpath = os.path.join(path, tarinfo.name)
2266*cda5da8dSAndroid Build Coastguard Worker            try:
2267*cda5da8dSAndroid Build Coastguard Worker                self.chown(tarinfo, dirpath, numeric_owner=numeric_owner)
2268*cda5da8dSAndroid Build Coastguard Worker                self.utime(tarinfo, dirpath)
2269*cda5da8dSAndroid Build Coastguard Worker                self.chmod(tarinfo, dirpath)
2270*cda5da8dSAndroid Build Coastguard Worker            except ExtractError as e:
2271*cda5da8dSAndroid Build Coastguard Worker                self._handle_nonfatal_error(e)
2272*cda5da8dSAndroid Build Coastguard Worker
2273*cda5da8dSAndroid Build Coastguard Worker    def extract(self, member, path="", set_attrs=True, *, numeric_owner=False,
2274*cda5da8dSAndroid Build Coastguard Worker                filter=None):
2275*cda5da8dSAndroid Build Coastguard Worker        """Extract a member from the archive to the current working directory,
2276*cda5da8dSAndroid Build Coastguard Worker           using its full name. Its file information is extracted as accurately
2277*cda5da8dSAndroid Build Coastguard Worker           as possible. `member' may be a filename or a TarInfo object. You can
2278*cda5da8dSAndroid Build Coastguard Worker           specify a different directory using `path'. File attributes (owner,
2279*cda5da8dSAndroid Build Coastguard Worker           mtime, mode) are set unless `set_attrs' is False. If `numeric_owner`
2280*cda5da8dSAndroid Build Coastguard Worker           is True, only the numbers for user/group names are used and not
2281*cda5da8dSAndroid Build Coastguard Worker           the names.
2282*cda5da8dSAndroid Build Coastguard Worker
2283*cda5da8dSAndroid Build Coastguard Worker           The `filter` function will be called before extraction.
2284*cda5da8dSAndroid Build Coastguard Worker           It can return a changed TarInfo or None to skip the member.
2285*cda5da8dSAndroid Build Coastguard Worker           String names of common filters are accepted.
2286*cda5da8dSAndroid Build Coastguard Worker        """
2287*cda5da8dSAndroid Build Coastguard Worker        filter_function = self._get_filter_function(filter)
2288*cda5da8dSAndroid Build Coastguard Worker        tarinfo = self._get_extract_tarinfo(member, filter_function, path)
2289*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is not None:
2290*cda5da8dSAndroid Build Coastguard Worker            self._extract_one(tarinfo, path, set_attrs, numeric_owner)
2291*cda5da8dSAndroid Build Coastguard Worker
2292*cda5da8dSAndroid Build Coastguard Worker    def _get_extract_tarinfo(self, member, filter_function, path):
2293*cda5da8dSAndroid Build Coastguard Worker        """Get filtered TarInfo (or None) from member, which might be a str"""
2294*cda5da8dSAndroid Build Coastguard Worker        if isinstance(member, str):
2295*cda5da8dSAndroid Build Coastguard Worker            tarinfo = self.getmember(member)
2296*cda5da8dSAndroid Build Coastguard Worker        else:
2297*cda5da8dSAndroid Build Coastguard Worker            tarinfo = member
2298*cda5da8dSAndroid Build Coastguard Worker
2299*cda5da8dSAndroid Build Coastguard Worker        unfiltered = tarinfo
2300*cda5da8dSAndroid Build Coastguard Worker        try:
2301*cda5da8dSAndroid Build Coastguard Worker            tarinfo = filter_function(tarinfo, path)
2302*cda5da8dSAndroid Build Coastguard Worker        except (OSError, FilterError) as e:
2303*cda5da8dSAndroid Build Coastguard Worker            self._handle_fatal_error(e)
2304*cda5da8dSAndroid Build Coastguard Worker        except ExtractError as e:
2305*cda5da8dSAndroid Build Coastguard Worker            self._handle_nonfatal_error(e)
2306*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is None:
2307*cda5da8dSAndroid Build Coastguard Worker            self._dbg(2, "tarfile: Excluded %r" % unfiltered.name)
2308*cda5da8dSAndroid Build Coastguard Worker            return None
2309*cda5da8dSAndroid Build Coastguard Worker        # Prepare the link target for makelink().
2310*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.islnk():
2311*cda5da8dSAndroid Build Coastguard Worker            tarinfo = copy.copy(tarinfo)
2312*cda5da8dSAndroid Build Coastguard Worker            tarinfo._link_target = os.path.join(path, tarinfo.linkname)
2313*cda5da8dSAndroid Build Coastguard Worker        return tarinfo
2314*cda5da8dSAndroid Build Coastguard Worker
2315*cda5da8dSAndroid Build Coastguard Worker    def _extract_one(self, tarinfo, path, set_attrs, numeric_owner):
2316*cda5da8dSAndroid Build Coastguard Worker        """Extract from filtered tarinfo to disk"""
2317*cda5da8dSAndroid Build Coastguard Worker        self._check("r")
2318*cda5da8dSAndroid Build Coastguard Worker
2319*cda5da8dSAndroid Build Coastguard Worker        try:
2320*cda5da8dSAndroid Build Coastguard Worker            self._extract_member(tarinfo, os.path.join(path, tarinfo.name),
2321*cda5da8dSAndroid Build Coastguard Worker                                 set_attrs=set_attrs,
2322*cda5da8dSAndroid Build Coastguard Worker                                 numeric_owner=numeric_owner)
2323*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
2324*cda5da8dSAndroid Build Coastguard Worker            self._handle_fatal_error(e)
2325*cda5da8dSAndroid Build Coastguard Worker        except ExtractError as e:
2326*cda5da8dSAndroid Build Coastguard Worker            self._handle_nonfatal_error(e)
2327*cda5da8dSAndroid Build Coastguard Worker
2328*cda5da8dSAndroid Build Coastguard Worker    def _handle_nonfatal_error(self, e):
2329*cda5da8dSAndroid Build Coastguard Worker        """Handle non-fatal error (ExtractError) according to errorlevel"""
2330*cda5da8dSAndroid Build Coastguard Worker        if self.errorlevel > 1:
2331*cda5da8dSAndroid Build Coastguard Worker            raise
2332*cda5da8dSAndroid Build Coastguard Worker        else:
2333*cda5da8dSAndroid Build Coastguard Worker            self._dbg(1, "tarfile: %s" % e)
2334*cda5da8dSAndroid Build Coastguard Worker
2335*cda5da8dSAndroid Build Coastguard Worker    def _handle_fatal_error(self, e):
2336*cda5da8dSAndroid Build Coastguard Worker        """Handle "fatal" error according to self.errorlevel"""
2337*cda5da8dSAndroid Build Coastguard Worker        if self.errorlevel > 0:
2338*cda5da8dSAndroid Build Coastguard Worker            raise
2339*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(e, OSError):
2340*cda5da8dSAndroid Build Coastguard Worker            if e.filename is None:
2341*cda5da8dSAndroid Build Coastguard Worker                self._dbg(1, "tarfile: %s" % e.strerror)
2342*cda5da8dSAndroid Build Coastguard Worker            else:
2343*cda5da8dSAndroid Build Coastguard Worker                self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename))
2344*cda5da8dSAndroid Build Coastguard Worker        else:
2345*cda5da8dSAndroid Build Coastguard Worker            self._dbg(1, "tarfile: %s %s" % (type(e).__name__, e))
2346*cda5da8dSAndroid Build Coastguard Worker
2347*cda5da8dSAndroid Build Coastguard Worker    def extractfile(self, member):
2348*cda5da8dSAndroid Build Coastguard Worker        """Extract a member from the archive as a file object. `member' may be
2349*cda5da8dSAndroid Build Coastguard Worker           a filename or a TarInfo object. If `member' is a regular file or
2350*cda5da8dSAndroid Build Coastguard Worker           a link, an io.BufferedReader object is returned. For all other
2351*cda5da8dSAndroid Build Coastguard Worker           existing members, None is returned. If `member' does not appear
2352*cda5da8dSAndroid Build Coastguard Worker           in the archive, KeyError is raised.
2353*cda5da8dSAndroid Build Coastguard Worker        """
2354*cda5da8dSAndroid Build Coastguard Worker        self._check("r")
2355*cda5da8dSAndroid Build Coastguard Worker
2356*cda5da8dSAndroid Build Coastguard Worker        if isinstance(member, str):
2357*cda5da8dSAndroid Build Coastguard Worker            tarinfo = self.getmember(member)
2358*cda5da8dSAndroid Build Coastguard Worker        else:
2359*cda5da8dSAndroid Build Coastguard Worker            tarinfo = member
2360*cda5da8dSAndroid Build Coastguard Worker
2361*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES:
2362*cda5da8dSAndroid Build Coastguard Worker            # Members with unknown types are treated as regular files.
2363*cda5da8dSAndroid Build Coastguard Worker            return self.fileobject(self, tarinfo)
2364*cda5da8dSAndroid Build Coastguard Worker
2365*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.islnk() or tarinfo.issym():
2366*cda5da8dSAndroid Build Coastguard Worker            if isinstance(self.fileobj, _Stream):
2367*cda5da8dSAndroid Build Coastguard Worker                # A small but ugly workaround for the case that someone tries
2368*cda5da8dSAndroid Build Coastguard Worker                # to extract a (sym)link as a file-object from a non-seekable
2369*cda5da8dSAndroid Build Coastguard Worker                # stream of tar blocks.
2370*cda5da8dSAndroid Build Coastguard Worker                raise StreamError("cannot extract (sym)link as file object")
2371*cda5da8dSAndroid Build Coastguard Worker            else:
2372*cda5da8dSAndroid Build Coastguard Worker                # A (sym)link's file object is its target's file object.
2373*cda5da8dSAndroid Build Coastguard Worker                return self.extractfile(self._find_link_target(tarinfo))
2374*cda5da8dSAndroid Build Coastguard Worker        else:
2375*cda5da8dSAndroid Build Coastguard Worker            # If there's no data associated with the member (directory, chrdev,
2376*cda5da8dSAndroid Build Coastguard Worker            # blkdev, etc.), return None instead of a file object.
2377*cda5da8dSAndroid Build Coastguard Worker            return None
2378*cda5da8dSAndroid Build Coastguard Worker
2379*cda5da8dSAndroid Build Coastguard Worker    def _extract_member(self, tarinfo, targetpath, set_attrs=True,
2380*cda5da8dSAndroid Build Coastguard Worker                        numeric_owner=False):
2381*cda5da8dSAndroid Build Coastguard Worker        """Extract the TarInfo object tarinfo to a physical
2382*cda5da8dSAndroid Build Coastguard Worker           file called targetpath.
2383*cda5da8dSAndroid Build Coastguard Worker        """
2384*cda5da8dSAndroid Build Coastguard Worker        # Fetch the TarInfo object for the given name
2385*cda5da8dSAndroid Build Coastguard Worker        # and build the destination pathname, replacing
2386*cda5da8dSAndroid Build Coastguard Worker        # forward slashes to platform specific separators.
2387*cda5da8dSAndroid Build Coastguard Worker        targetpath = targetpath.rstrip("/")
2388*cda5da8dSAndroid Build Coastguard Worker        targetpath = targetpath.replace("/", os.sep)
2389*cda5da8dSAndroid Build Coastguard Worker
2390*cda5da8dSAndroid Build Coastguard Worker        # Create all upper directories.
2391*cda5da8dSAndroid Build Coastguard Worker        upperdirs = os.path.dirname(targetpath)
2392*cda5da8dSAndroid Build Coastguard Worker        if upperdirs and not os.path.exists(upperdirs):
2393*cda5da8dSAndroid Build Coastguard Worker            # Create directories that are not part of the archive with
2394*cda5da8dSAndroid Build Coastguard Worker            # default permissions.
2395*cda5da8dSAndroid Build Coastguard Worker            os.makedirs(upperdirs)
2396*cda5da8dSAndroid Build Coastguard Worker
2397*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.islnk() or tarinfo.issym():
2398*cda5da8dSAndroid Build Coastguard Worker            self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname))
2399*cda5da8dSAndroid Build Coastguard Worker        else:
2400*cda5da8dSAndroid Build Coastguard Worker            self._dbg(1, tarinfo.name)
2401*cda5da8dSAndroid Build Coastguard Worker
2402*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.isreg():
2403*cda5da8dSAndroid Build Coastguard Worker            self.makefile(tarinfo, targetpath)
2404*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.isdir():
2405*cda5da8dSAndroid Build Coastguard Worker            self.makedir(tarinfo, targetpath)
2406*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.isfifo():
2407*cda5da8dSAndroid Build Coastguard Worker            self.makefifo(tarinfo, targetpath)
2408*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.ischr() or tarinfo.isblk():
2409*cda5da8dSAndroid Build Coastguard Worker            self.makedev(tarinfo, targetpath)
2410*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.islnk() or tarinfo.issym():
2411*cda5da8dSAndroid Build Coastguard Worker            self.makelink(tarinfo, targetpath)
2412*cda5da8dSAndroid Build Coastguard Worker        elif tarinfo.type not in SUPPORTED_TYPES:
2413*cda5da8dSAndroid Build Coastguard Worker            self.makeunknown(tarinfo, targetpath)
2414*cda5da8dSAndroid Build Coastguard Worker        else:
2415*cda5da8dSAndroid Build Coastguard Worker            self.makefile(tarinfo, targetpath)
2416*cda5da8dSAndroid Build Coastguard Worker
2417*cda5da8dSAndroid Build Coastguard Worker        if set_attrs:
2418*cda5da8dSAndroid Build Coastguard Worker            self.chown(tarinfo, targetpath, numeric_owner)
2419*cda5da8dSAndroid Build Coastguard Worker            if not tarinfo.issym():
2420*cda5da8dSAndroid Build Coastguard Worker                self.chmod(tarinfo, targetpath)
2421*cda5da8dSAndroid Build Coastguard Worker                self.utime(tarinfo, targetpath)
2422*cda5da8dSAndroid Build Coastguard Worker
2423*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
2424*cda5da8dSAndroid Build Coastguard Worker    # Below are the different file methods. They are called via
2425*cda5da8dSAndroid Build Coastguard Worker    # _extract_member() when extract() is called. They can be replaced in a
2426*cda5da8dSAndroid Build Coastguard Worker    # subclass to implement other functionality.
2427*cda5da8dSAndroid Build Coastguard Worker
2428*cda5da8dSAndroid Build Coastguard Worker    def makedir(self, tarinfo, targetpath):
2429*cda5da8dSAndroid Build Coastguard Worker        """Make a directory called targetpath.
2430*cda5da8dSAndroid Build Coastguard Worker        """
2431*cda5da8dSAndroid Build Coastguard Worker        try:
2432*cda5da8dSAndroid Build Coastguard Worker            if tarinfo.mode is None:
2433*cda5da8dSAndroid Build Coastguard Worker                # Use the system's default mode
2434*cda5da8dSAndroid Build Coastguard Worker                os.mkdir(targetpath)
2435*cda5da8dSAndroid Build Coastguard Worker            else:
2436*cda5da8dSAndroid Build Coastguard Worker                # Use a safe mode for the directory, the real mode is set
2437*cda5da8dSAndroid Build Coastguard Worker                # later in _extract_member().
2438*cda5da8dSAndroid Build Coastguard Worker                os.mkdir(targetpath, 0o700)
2439*cda5da8dSAndroid Build Coastguard Worker        except FileExistsError:
2440*cda5da8dSAndroid Build Coastguard Worker            pass
2441*cda5da8dSAndroid Build Coastguard Worker
2442*cda5da8dSAndroid Build Coastguard Worker    def makefile(self, tarinfo, targetpath):
2443*cda5da8dSAndroid Build Coastguard Worker        """Make a file called targetpath.
2444*cda5da8dSAndroid Build Coastguard Worker        """
2445*cda5da8dSAndroid Build Coastguard Worker        source = self.fileobj
2446*cda5da8dSAndroid Build Coastguard Worker        source.seek(tarinfo.offset_data)
2447*cda5da8dSAndroid Build Coastguard Worker        bufsize = self.copybufsize
2448*cda5da8dSAndroid Build Coastguard Worker        with bltn_open(targetpath, "wb") as target:
2449*cda5da8dSAndroid Build Coastguard Worker            if tarinfo.sparse is not None:
2450*cda5da8dSAndroid Build Coastguard Worker                for offset, size in tarinfo.sparse:
2451*cda5da8dSAndroid Build Coastguard Worker                    target.seek(offset)
2452*cda5da8dSAndroid Build Coastguard Worker                    copyfileobj(source, target, size, ReadError, bufsize)
2453*cda5da8dSAndroid Build Coastguard Worker                target.seek(tarinfo.size)
2454*cda5da8dSAndroid Build Coastguard Worker                target.truncate()
2455*cda5da8dSAndroid Build Coastguard Worker            else:
2456*cda5da8dSAndroid Build Coastguard Worker                copyfileobj(source, target, tarinfo.size, ReadError, bufsize)
2457*cda5da8dSAndroid Build Coastguard Worker
2458*cda5da8dSAndroid Build Coastguard Worker    def makeunknown(self, tarinfo, targetpath):
2459*cda5da8dSAndroid Build Coastguard Worker        """Make a file from a TarInfo object with an unknown type
2460*cda5da8dSAndroid Build Coastguard Worker           at targetpath.
2461*cda5da8dSAndroid Build Coastguard Worker        """
2462*cda5da8dSAndroid Build Coastguard Worker        self.makefile(tarinfo, targetpath)
2463*cda5da8dSAndroid Build Coastguard Worker        self._dbg(1, "tarfile: Unknown file type %r, " \
2464*cda5da8dSAndroid Build Coastguard Worker                     "extracted as regular file." % tarinfo.type)
2465*cda5da8dSAndroid Build Coastguard Worker
2466*cda5da8dSAndroid Build Coastguard Worker    def makefifo(self, tarinfo, targetpath):
2467*cda5da8dSAndroid Build Coastguard Worker        """Make a fifo called targetpath.
2468*cda5da8dSAndroid Build Coastguard Worker        """
2469*cda5da8dSAndroid Build Coastguard Worker        if hasattr(os, "mkfifo"):
2470*cda5da8dSAndroid Build Coastguard Worker            os.mkfifo(targetpath)
2471*cda5da8dSAndroid Build Coastguard Worker        else:
2472*cda5da8dSAndroid Build Coastguard Worker            raise ExtractError("fifo not supported by system")
2473*cda5da8dSAndroid Build Coastguard Worker
2474*cda5da8dSAndroid Build Coastguard Worker    def makedev(self, tarinfo, targetpath):
2475*cda5da8dSAndroid Build Coastguard Worker        """Make a character or block device called targetpath.
2476*cda5da8dSAndroid Build Coastguard Worker        """
2477*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(os, "mknod") or not hasattr(os, "makedev"):
2478*cda5da8dSAndroid Build Coastguard Worker            raise ExtractError("special devices not supported by system")
2479*cda5da8dSAndroid Build Coastguard Worker
2480*cda5da8dSAndroid Build Coastguard Worker        mode = tarinfo.mode
2481*cda5da8dSAndroid Build Coastguard Worker        if mode is None:
2482*cda5da8dSAndroid Build Coastguard Worker            # Use mknod's default
2483*cda5da8dSAndroid Build Coastguard Worker            mode = 0o600
2484*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.isblk():
2485*cda5da8dSAndroid Build Coastguard Worker            mode |= stat.S_IFBLK
2486*cda5da8dSAndroid Build Coastguard Worker        else:
2487*cda5da8dSAndroid Build Coastguard Worker            mode |= stat.S_IFCHR
2488*cda5da8dSAndroid Build Coastguard Worker
2489*cda5da8dSAndroid Build Coastguard Worker        os.mknod(targetpath, mode,
2490*cda5da8dSAndroid Build Coastguard Worker                 os.makedev(tarinfo.devmajor, tarinfo.devminor))
2491*cda5da8dSAndroid Build Coastguard Worker
2492*cda5da8dSAndroid Build Coastguard Worker    def makelink(self, tarinfo, targetpath):
2493*cda5da8dSAndroid Build Coastguard Worker        """Make a (symbolic) link called targetpath. If it cannot be created
2494*cda5da8dSAndroid Build Coastguard Worker          (platform limitation), we try to make a copy of the referenced file
2495*cda5da8dSAndroid Build Coastguard Worker          instead of a link.
2496*cda5da8dSAndroid Build Coastguard Worker        """
2497*cda5da8dSAndroid Build Coastguard Worker        try:
2498*cda5da8dSAndroid Build Coastguard Worker            # For systems that support symbolic and hard links.
2499*cda5da8dSAndroid Build Coastguard Worker            if tarinfo.issym():
2500*cda5da8dSAndroid Build Coastguard Worker                if os.path.lexists(targetpath):
2501*cda5da8dSAndroid Build Coastguard Worker                    # Avoid FileExistsError on following os.symlink.
2502*cda5da8dSAndroid Build Coastguard Worker                    os.unlink(targetpath)
2503*cda5da8dSAndroid Build Coastguard Worker                os.symlink(tarinfo.linkname, targetpath)
2504*cda5da8dSAndroid Build Coastguard Worker            else:
2505*cda5da8dSAndroid Build Coastguard Worker                if os.path.exists(tarinfo._link_target):
2506*cda5da8dSAndroid Build Coastguard Worker                    os.link(tarinfo._link_target, targetpath)
2507*cda5da8dSAndroid Build Coastguard Worker                else:
2508*cda5da8dSAndroid Build Coastguard Worker                    self._extract_member(self._find_link_target(tarinfo),
2509*cda5da8dSAndroid Build Coastguard Worker                                         targetpath)
2510*cda5da8dSAndroid Build Coastguard Worker        except symlink_exception:
2511*cda5da8dSAndroid Build Coastguard Worker            try:
2512*cda5da8dSAndroid Build Coastguard Worker                self._extract_member(self._find_link_target(tarinfo),
2513*cda5da8dSAndroid Build Coastguard Worker                                     targetpath)
2514*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
2515*cda5da8dSAndroid Build Coastguard Worker                raise ExtractError("unable to resolve link inside archive") from None
2516*cda5da8dSAndroid Build Coastguard Worker
2517*cda5da8dSAndroid Build Coastguard Worker    def chown(self, tarinfo, targetpath, numeric_owner):
2518*cda5da8dSAndroid Build Coastguard Worker        """Set owner of targetpath according to tarinfo. If numeric_owner
2519*cda5da8dSAndroid Build Coastguard Worker           is True, use .gid/.uid instead of .gname/.uname. If numeric_owner
2520*cda5da8dSAndroid Build Coastguard Worker           is False, fall back to .gid/.uid when the search based on name
2521*cda5da8dSAndroid Build Coastguard Worker           fails.
2522*cda5da8dSAndroid Build Coastguard Worker        """
2523*cda5da8dSAndroid Build Coastguard Worker        if hasattr(os, "geteuid") and os.geteuid() == 0:
2524*cda5da8dSAndroid Build Coastguard Worker            # We have to be root to do so.
2525*cda5da8dSAndroid Build Coastguard Worker            g = tarinfo.gid
2526*cda5da8dSAndroid Build Coastguard Worker            u = tarinfo.uid
2527*cda5da8dSAndroid Build Coastguard Worker            if not numeric_owner:
2528*cda5da8dSAndroid Build Coastguard Worker                try:
2529*cda5da8dSAndroid Build Coastguard Worker                    if grp and tarinfo.gname:
2530*cda5da8dSAndroid Build Coastguard Worker                        g = grp.getgrnam(tarinfo.gname)[2]
2531*cda5da8dSAndroid Build Coastguard Worker                except KeyError:
2532*cda5da8dSAndroid Build Coastguard Worker                    pass
2533*cda5da8dSAndroid Build Coastguard Worker                try:
2534*cda5da8dSAndroid Build Coastguard Worker                    if pwd and tarinfo.uname:
2535*cda5da8dSAndroid Build Coastguard Worker                        u = pwd.getpwnam(tarinfo.uname)[2]
2536*cda5da8dSAndroid Build Coastguard Worker                except KeyError:
2537*cda5da8dSAndroid Build Coastguard Worker                    pass
2538*cda5da8dSAndroid Build Coastguard Worker            if g is None:
2539*cda5da8dSAndroid Build Coastguard Worker                g = -1
2540*cda5da8dSAndroid Build Coastguard Worker            if u is None:
2541*cda5da8dSAndroid Build Coastguard Worker                u = -1
2542*cda5da8dSAndroid Build Coastguard Worker            try:
2543*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.issym() and hasattr(os, "lchown"):
2544*cda5da8dSAndroid Build Coastguard Worker                    os.lchown(targetpath, u, g)
2545*cda5da8dSAndroid Build Coastguard Worker                else:
2546*cda5da8dSAndroid Build Coastguard Worker                    os.chown(targetpath, u, g)
2547*cda5da8dSAndroid Build Coastguard Worker            except OSError as e:
2548*cda5da8dSAndroid Build Coastguard Worker                raise ExtractError("could not change owner") from e
2549*cda5da8dSAndroid Build Coastguard Worker
2550*cda5da8dSAndroid Build Coastguard Worker    def chmod(self, tarinfo, targetpath):
2551*cda5da8dSAndroid Build Coastguard Worker        """Set file permissions of targetpath according to tarinfo.
2552*cda5da8dSAndroid Build Coastguard Worker        """
2553*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.mode is None:
2554*cda5da8dSAndroid Build Coastguard Worker            return
2555*cda5da8dSAndroid Build Coastguard Worker        try:
2556*cda5da8dSAndroid Build Coastguard Worker            os.chmod(targetpath, tarinfo.mode)
2557*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
2558*cda5da8dSAndroid Build Coastguard Worker            raise ExtractError("could not change mode") from e
2559*cda5da8dSAndroid Build Coastguard Worker
2560*cda5da8dSAndroid Build Coastguard Worker    def utime(self, tarinfo, targetpath):
2561*cda5da8dSAndroid Build Coastguard Worker        """Set modification time of targetpath according to tarinfo.
2562*cda5da8dSAndroid Build Coastguard Worker        """
2563*cda5da8dSAndroid Build Coastguard Worker        mtime = tarinfo.mtime
2564*cda5da8dSAndroid Build Coastguard Worker        if mtime is None:
2565*cda5da8dSAndroid Build Coastguard Worker            return
2566*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(os, 'utime'):
2567*cda5da8dSAndroid Build Coastguard Worker            return
2568*cda5da8dSAndroid Build Coastguard Worker        try:
2569*cda5da8dSAndroid Build Coastguard Worker            os.utime(targetpath, (mtime, mtime))
2570*cda5da8dSAndroid Build Coastguard Worker        except OSError as e:
2571*cda5da8dSAndroid Build Coastguard Worker            raise ExtractError("could not change modification time") from e
2572*cda5da8dSAndroid Build Coastguard Worker
2573*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
2574*cda5da8dSAndroid Build Coastguard Worker    def next(self):
2575*cda5da8dSAndroid Build Coastguard Worker        """Return the next member of the archive as a TarInfo object, when
2576*cda5da8dSAndroid Build Coastguard Worker           TarFile is opened for reading. Return None if there is no more
2577*cda5da8dSAndroid Build Coastguard Worker           available.
2578*cda5da8dSAndroid Build Coastguard Worker        """
2579*cda5da8dSAndroid Build Coastguard Worker        self._check("ra")
2580*cda5da8dSAndroid Build Coastguard Worker        if self.firstmember is not None:
2581*cda5da8dSAndroid Build Coastguard Worker            m = self.firstmember
2582*cda5da8dSAndroid Build Coastguard Worker            self.firstmember = None
2583*cda5da8dSAndroid Build Coastguard Worker            return m
2584*cda5da8dSAndroid Build Coastguard Worker
2585*cda5da8dSAndroid Build Coastguard Worker        # Advance the file pointer.
2586*cda5da8dSAndroid Build Coastguard Worker        if self.offset != self.fileobj.tell():
2587*cda5da8dSAndroid Build Coastguard Worker            if self.offset == 0:
2588*cda5da8dSAndroid Build Coastguard Worker                return None
2589*cda5da8dSAndroid Build Coastguard Worker            self.fileobj.seek(self.offset - 1)
2590*cda5da8dSAndroid Build Coastguard Worker            if not self.fileobj.read(1):
2591*cda5da8dSAndroid Build Coastguard Worker                raise ReadError("unexpected end of data")
2592*cda5da8dSAndroid Build Coastguard Worker
2593*cda5da8dSAndroid Build Coastguard Worker        # Read the next block.
2594*cda5da8dSAndroid Build Coastguard Worker        tarinfo = None
2595*cda5da8dSAndroid Build Coastguard Worker        while True:
2596*cda5da8dSAndroid Build Coastguard Worker            try:
2597*cda5da8dSAndroid Build Coastguard Worker                tarinfo = self.tarinfo.fromtarfile(self)
2598*cda5da8dSAndroid Build Coastguard Worker            except EOFHeaderError as e:
2599*cda5da8dSAndroid Build Coastguard Worker                if self.ignore_zeros:
2600*cda5da8dSAndroid Build Coastguard Worker                    self._dbg(2, "0x%X: %s" % (self.offset, e))
2601*cda5da8dSAndroid Build Coastguard Worker                    self.offset += BLOCKSIZE
2602*cda5da8dSAndroid Build Coastguard Worker                    continue
2603*cda5da8dSAndroid Build Coastguard Worker            except InvalidHeaderError as e:
2604*cda5da8dSAndroid Build Coastguard Worker                if self.ignore_zeros:
2605*cda5da8dSAndroid Build Coastguard Worker                    self._dbg(2, "0x%X: %s" % (self.offset, e))
2606*cda5da8dSAndroid Build Coastguard Worker                    self.offset += BLOCKSIZE
2607*cda5da8dSAndroid Build Coastguard Worker                    continue
2608*cda5da8dSAndroid Build Coastguard Worker                elif self.offset == 0:
2609*cda5da8dSAndroid Build Coastguard Worker                    raise ReadError(str(e)) from None
2610*cda5da8dSAndroid Build Coastguard Worker            except EmptyHeaderError:
2611*cda5da8dSAndroid Build Coastguard Worker                if self.offset == 0:
2612*cda5da8dSAndroid Build Coastguard Worker                    raise ReadError("empty file") from None
2613*cda5da8dSAndroid Build Coastguard Worker            except TruncatedHeaderError as e:
2614*cda5da8dSAndroid Build Coastguard Worker                if self.offset == 0:
2615*cda5da8dSAndroid Build Coastguard Worker                    raise ReadError(str(e)) from None
2616*cda5da8dSAndroid Build Coastguard Worker            except SubsequentHeaderError as e:
2617*cda5da8dSAndroid Build Coastguard Worker                raise ReadError(str(e)) from None
2618*cda5da8dSAndroid Build Coastguard Worker            except Exception as e:
2619*cda5da8dSAndroid Build Coastguard Worker                try:
2620*cda5da8dSAndroid Build Coastguard Worker                    import zlib
2621*cda5da8dSAndroid Build Coastguard Worker                    if isinstance(e, zlib.error):
2622*cda5da8dSAndroid Build Coastguard Worker                        raise ReadError(f'zlib error: {e}') from None
2623*cda5da8dSAndroid Build Coastguard Worker                    else:
2624*cda5da8dSAndroid Build Coastguard Worker                        raise e
2625*cda5da8dSAndroid Build Coastguard Worker                except ImportError:
2626*cda5da8dSAndroid Build Coastguard Worker                    raise e
2627*cda5da8dSAndroid Build Coastguard Worker            break
2628*cda5da8dSAndroid Build Coastguard Worker
2629*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is not None:
2630*cda5da8dSAndroid Build Coastguard Worker            self.members.append(tarinfo)
2631*cda5da8dSAndroid Build Coastguard Worker        else:
2632*cda5da8dSAndroid Build Coastguard Worker            self._loaded = True
2633*cda5da8dSAndroid Build Coastguard Worker
2634*cda5da8dSAndroid Build Coastguard Worker        return tarinfo
2635*cda5da8dSAndroid Build Coastguard Worker
2636*cda5da8dSAndroid Build Coastguard Worker    #--------------------------------------------------------------------------
2637*cda5da8dSAndroid Build Coastguard Worker    # Little helper methods:
2638*cda5da8dSAndroid Build Coastguard Worker
2639*cda5da8dSAndroid Build Coastguard Worker    def _getmember(self, name, tarinfo=None, normalize=False):
2640*cda5da8dSAndroid Build Coastguard Worker        """Find an archive member by name from bottom to top.
2641*cda5da8dSAndroid Build Coastguard Worker           If tarinfo is given, it is used as the starting point.
2642*cda5da8dSAndroid Build Coastguard Worker        """
2643*cda5da8dSAndroid Build Coastguard Worker        # Ensure that all members have been loaded.
2644*cda5da8dSAndroid Build Coastguard Worker        members = self.getmembers()
2645*cda5da8dSAndroid Build Coastguard Worker
2646*cda5da8dSAndroid Build Coastguard Worker        # Limit the member search list up to tarinfo.
2647*cda5da8dSAndroid Build Coastguard Worker        skipping = False
2648*cda5da8dSAndroid Build Coastguard Worker        if tarinfo is not None:
2649*cda5da8dSAndroid Build Coastguard Worker            try:
2650*cda5da8dSAndroid Build Coastguard Worker                index = members.index(tarinfo)
2651*cda5da8dSAndroid Build Coastguard Worker            except ValueError:
2652*cda5da8dSAndroid Build Coastguard Worker                # The given starting point might be a (modified) copy.
2653*cda5da8dSAndroid Build Coastguard Worker                # We'll later skip members until we find an equivalent.
2654*cda5da8dSAndroid Build Coastguard Worker                skipping = True
2655*cda5da8dSAndroid Build Coastguard Worker            else:
2656*cda5da8dSAndroid Build Coastguard Worker                # Happy fast path
2657*cda5da8dSAndroid Build Coastguard Worker                members = members[:index]
2658*cda5da8dSAndroid Build Coastguard Worker
2659*cda5da8dSAndroid Build Coastguard Worker        if normalize:
2660*cda5da8dSAndroid Build Coastguard Worker            name = os.path.normpath(name)
2661*cda5da8dSAndroid Build Coastguard Worker
2662*cda5da8dSAndroid Build Coastguard Worker        for member in reversed(members):
2663*cda5da8dSAndroid Build Coastguard Worker            if skipping:
2664*cda5da8dSAndroid Build Coastguard Worker                if tarinfo.offset == member.offset:
2665*cda5da8dSAndroid Build Coastguard Worker                    skipping = False
2666*cda5da8dSAndroid Build Coastguard Worker                continue
2667*cda5da8dSAndroid Build Coastguard Worker            if normalize:
2668*cda5da8dSAndroid Build Coastguard Worker                member_name = os.path.normpath(member.name)
2669*cda5da8dSAndroid Build Coastguard Worker            else:
2670*cda5da8dSAndroid Build Coastguard Worker                member_name = member.name
2671*cda5da8dSAndroid Build Coastguard Worker
2672*cda5da8dSAndroid Build Coastguard Worker            if name == member_name:
2673*cda5da8dSAndroid Build Coastguard Worker                return member
2674*cda5da8dSAndroid Build Coastguard Worker
2675*cda5da8dSAndroid Build Coastguard Worker        if skipping:
2676*cda5da8dSAndroid Build Coastguard Worker            # Starting point was not found
2677*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(tarinfo)
2678*cda5da8dSAndroid Build Coastguard Worker
2679*cda5da8dSAndroid Build Coastguard Worker    def _load(self):
2680*cda5da8dSAndroid Build Coastguard Worker        """Read through the entire archive file and look for readable
2681*cda5da8dSAndroid Build Coastguard Worker           members.
2682*cda5da8dSAndroid Build Coastguard Worker        """
2683*cda5da8dSAndroid Build Coastguard Worker        while True:
2684*cda5da8dSAndroid Build Coastguard Worker            tarinfo = self.next()
2685*cda5da8dSAndroid Build Coastguard Worker            if tarinfo is None:
2686*cda5da8dSAndroid Build Coastguard Worker                break
2687*cda5da8dSAndroid Build Coastguard Worker        self._loaded = True
2688*cda5da8dSAndroid Build Coastguard Worker
2689*cda5da8dSAndroid Build Coastguard Worker    def _check(self, mode=None):
2690*cda5da8dSAndroid Build Coastguard Worker        """Check if TarFile is still open, and if the operation's mode
2691*cda5da8dSAndroid Build Coastguard Worker           corresponds to TarFile's mode.
2692*cda5da8dSAndroid Build Coastguard Worker        """
2693*cda5da8dSAndroid Build Coastguard Worker        if self.closed:
2694*cda5da8dSAndroid Build Coastguard Worker            raise OSError("%s is closed" % self.__class__.__name__)
2695*cda5da8dSAndroid Build Coastguard Worker        if mode is not None and self.mode not in mode:
2696*cda5da8dSAndroid Build Coastguard Worker            raise OSError("bad operation for mode %r" % self.mode)
2697*cda5da8dSAndroid Build Coastguard Worker
2698*cda5da8dSAndroid Build Coastguard Worker    def _find_link_target(self, tarinfo):
2699*cda5da8dSAndroid Build Coastguard Worker        """Find the target member of a symlink or hardlink member in the
2700*cda5da8dSAndroid Build Coastguard Worker           archive.
2701*cda5da8dSAndroid Build Coastguard Worker        """
2702*cda5da8dSAndroid Build Coastguard Worker        if tarinfo.issym():
2703*cda5da8dSAndroid Build Coastguard Worker            # Always search the entire archive.
2704*cda5da8dSAndroid Build Coastguard Worker            linkname = "/".join(filter(None, (os.path.dirname(tarinfo.name), tarinfo.linkname)))
2705*cda5da8dSAndroid Build Coastguard Worker            limit = None
2706*cda5da8dSAndroid Build Coastguard Worker        else:
2707*cda5da8dSAndroid Build Coastguard Worker            # Search the archive before the link, because a hard link is
2708*cda5da8dSAndroid Build Coastguard Worker            # just a reference to an already archived file.
2709*cda5da8dSAndroid Build Coastguard Worker            linkname = tarinfo.linkname
2710*cda5da8dSAndroid Build Coastguard Worker            limit = tarinfo
2711*cda5da8dSAndroid Build Coastguard Worker
2712*cda5da8dSAndroid Build Coastguard Worker        member = self._getmember(linkname, tarinfo=limit, normalize=True)
2713*cda5da8dSAndroid Build Coastguard Worker        if member is None:
2714*cda5da8dSAndroid Build Coastguard Worker            raise KeyError("linkname %r not found" % linkname)
2715*cda5da8dSAndroid Build Coastguard Worker        return member
2716*cda5da8dSAndroid Build Coastguard Worker
2717*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
2718*cda5da8dSAndroid Build Coastguard Worker        """Provide an iterator object.
2719*cda5da8dSAndroid Build Coastguard Worker        """
2720*cda5da8dSAndroid Build Coastguard Worker        if self._loaded:
2721*cda5da8dSAndroid Build Coastguard Worker            yield from self.members
2722*cda5da8dSAndroid Build Coastguard Worker            return
2723*cda5da8dSAndroid Build Coastguard Worker
2724*cda5da8dSAndroid Build Coastguard Worker        # Yield items using TarFile's next() method.
2725*cda5da8dSAndroid Build Coastguard Worker        # When all members have been read, set TarFile as _loaded.
2726*cda5da8dSAndroid Build Coastguard Worker        index = 0
2727*cda5da8dSAndroid Build Coastguard Worker        # Fix for SF #1100429: Under rare circumstances it can
2728*cda5da8dSAndroid Build Coastguard Worker        # happen that getmembers() is called during iteration,
2729*cda5da8dSAndroid Build Coastguard Worker        # which will have already exhausted the next() method.
2730*cda5da8dSAndroid Build Coastguard Worker        if self.firstmember is not None:
2731*cda5da8dSAndroid Build Coastguard Worker            tarinfo = self.next()
2732*cda5da8dSAndroid Build Coastguard Worker            index += 1
2733*cda5da8dSAndroid Build Coastguard Worker            yield tarinfo
2734*cda5da8dSAndroid Build Coastguard Worker
2735*cda5da8dSAndroid Build Coastguard Worker        while True:
2736*cda5da8dSAndroid Build Coastguard Worker            if index < len(self.members):
2737*cda5da8dSAndroid Build Coastguard Worker                tarinfo = self.members[index]
2738*cda5da8dSAndroid Build Coastguard Worker            elif not self._loaded:
2739*cda5da8dSAndroid Build Coastguard Worker                tarinfo = self.next()
2740*cda5da8dSAndroid Build Coastguard Worker                if not tarinfo:
2741*cda5da8dSAndroid Build Coastguard Worker                    self._loaded = True
2742*cda5da8dSAndroid Build Coastguard Worker                    return
2743*cda5da8dSAndroid Build Coastguard Worker            else:
2744*cda5da8dSAndroid Build Coastguard Worker                return
2745*cda5da8dSAndroid Build Coastguard Worker            index += 1
2746*cda5da8dSAndroid Build Coastguard Worker            yield tarinfo
2747*cda5da8dSAndroid Build Coastguard Worker
2748*cda5da8dSAndroid Build Coastguard Worker    def _dbg(self, level, msg):
2749*cda5da8dSAndroid Build Coastguard Worker        """Write debugging output to sys.stderr.
2750*cda5da8dSAndroid Build Coastguard Worker        """
2751*cda5da8dSAndroid Build Coastguard Worker        if level <= self.debug:
2752*cda5da8dSAndroid Build Coastguard Worker            print(msg, file=sys.stderr)
2753*cda5da8dSAndroid Build Coastguard Worker
2754*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
2755*cda5da8dSAndroid Build Coastguard Worker        self._check()
2756*cda5da8dSAndroid Build Coastguard Worker        return self
2757*cda5da8dSAndroid Build Coastguard Worker
2758*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, type, value, traceback):
2759*cda5da8dSAndroid Build Coastguard Worker        if type is None:
2760*cda5da8dSAndroid Build Coastguard Worker            self.close()
2761*cda5da8dSAndroid Build Coastguard Worker        else:
2762*cda5da8dSAndroid Build Coastguard Worker            # An exception occurred. We must not call close() because
2763*cda5da8dSAndroid Build Coastguard Worker            # it would try to write end-of-archive blocks and padding.
2764*cda5da8dSAndroid Build Coastguard Worker            if not self._extfileobj:
2765*cda5da8dSAndroid Build Coastguard Worker                self.fileobj.close()
2766*cda5da8dSAndroid Build Coastguard Worker            self.closed = True
2767*cda5da8dSAndroid Build Coastguard Worker
2768*cda5da8dSAndroid Build Coastguard Worker#--------------------
2769*cda5da8dSAndroid Build Coastguard Worker# exported functions
2770*cda5da8dSAndroid Build Coastguard Worker#--------------------
2771*cda5da8dSAndroid Build Coastguard Worker
2772*cda5da8dSAndroid Build Coastguard Workerdef is_tarfile(name):
2773*cda5da8dSAndroid Build Coastguard Worker    """Return True if name points to a tar archive that we
2774*cda5da8dSAndroid Build Coastguard Worker       are able to handle, else return False.
2775*cda5da8dSAndroid Build Coastguard Worker
2776*cda5da8dSAndroid Build Coastguard Worker       'name' should be a string, file, or file-like object.
2777*cda5da8dSAndroid Build Coastguard Worker    """
2778*cda5da8dSAndroid Build Coastguard Worker    try:
2779*cda5da8dSAndroid Build Coastguard Worker        if hasattr(name, "read"):
2780*cda5da8dSAndroid Build Coastguard Worker            pos = name.tell()
2781*cda5da8dSAndroid Build Coastguard Worker            t = open(fileobj=name)
2782*cda5da8dSAndroid Build Coastguard Worker            name.seek(pos)
2783*cda5da8dSAndroid Build Coastguard Worker        else:
2784*cda5da8dSAndroid Build Coastguard Worker            t = open(name)
2785*cda5da8dSAndroid Build Coastguard Worker        t.close()
2786*cda5da8dSAndroid Build Coastguard Worker        return True
2787*cda5da8dSAndroid Build Coastguard Worker    except TarError:
2788*cda5da8dSAndroid Build Coastguard Worker        return False
2789*cda5da8dSAndroid Build Coastguard Worker
2790*cda5da8dSAndroid Build Coastguard Workeropen = TarFile.open
2791*cda5da8dSAndroid Build Coastguard Worker
2792*cda5da8dSAndroid Build Coastguard Worker
2793*cda5da8dSAndroid Build Coastguard Workerdef main():
2794*cda5da8dSAndroid Build Coastguard Worker    import argparse
2795*cda5da8dSAndroid Build Coastguard Worker
2796*cda5da8dSAndroid Build Coastguard Worker    description = 'A simple command-line interface for tarfile module.'
2797*cda5da8dSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description=description)
2798*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('-v', '--verbose', action='store_true', default=False,
2799*cda5da8dSAndroid Build Coastguard Worker                        help='Verbose output')
2800*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('--filter', metavar='<filtername>',
2801*cda5da8dSAndroid Build Coastguard Worker                        choices=_NAMED_FILTERS,
2802*cda5da8dSAndroid Build Coastguard Worker                        help='Filter for extraction')
2803*cda5da8dSAndroid Build Coastguard Worker
2804*cda5da8dSAndroid Build Coastguard Worker    group = parser.add_mutually_exclusive_group(required=True)
2805*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('-l', '--list', metavar='<tarfile>',
2806*cda5da8dSAndroid Build Coastguard Worker                       help='Show listing of a tarfile')
2807*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('-e', '--extract', nargs='+',
2808*cda5da8dSAndroid Build Coastguard Worker                       metavar=('<tarfile>', '<output_dir>'),
2809*cda5da8dSAndroid Build Coastguard Worker                       help='Extract tarfile into target dir')
2810*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('-c', '--create', nargs='+',
2811*cda5da8dSAndroid Build Coastguard Worker                       metavar=('<name>', '<file>'),
2812*cda5da8dSAndroid Build Coastguard Worker                       help='Create tarfile from sources')
2813*cda5da8dSAndroid Build Coastguard Worker    group.add_argument('-t', '--test', metavar='<tarfile>',
2814*cda5da8dSAndroid Build Coastguard Worker                       help='Test if a tarfile is valid')
2815*cda5da8dSAndroid Build Coastguard Worker
2816*cda5da8dSAndroid Build Coastguard Worker    args = parser.parse_args()
2817*cda5da8dSAndroid Build Coastguard Worker
2818*cda5da8dSAndroid Build Coastguard Worker    if args.filter and args.extract is None:
2819*cda5da8dSAndroid Build Coastguard Worker        parser.exit(1, '--filter is only valid for extraction\n')
2820*cda5da8dSAndroid Build Coastguard Worker
2821*cda5da8dSAndroid Build Coastguard Worker    if args.test is not None:
2822*cda5da8dSAndroid Build Coastguard Worker        src = args.test
2823*cda5da8dSAndroid Build Coastguard Worker        if is_tarfile(src):
2824*cda5da8dSAndroid Build Coastguard Worker            with open(src, 'r') as tar:
2825*cda5da8dSAndroid Build Coastguard Worker                tar.getmembers()
2826*cda5da8dSAndroid Build Coastguard Worker                print(tar.getmembers(), file=sys.stderr)
2827*cda5da8dSAndroid Build Coastguard Worker            if args.verbose:
2828*cda5da8dSAndroid Build Coastguard Worker                print('{!r} is a tar archive.'.format(src))
2829*cda5da8dSAndroid Build Coastguard Worker        else:
2830*cda5da8dSAndroid Build Coastguard Worker            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
2831*cda5da8dSAndroid Build Coastguard Worker
2832*cda5da8dSAndroid Build Coastguard Worker    elif args.list is not None:
2833*cda5da8dSAndroid Build Coastguard Worker        src = args.list
2834*cda5da8dSAndroid Build Coastguard Worker        if is_tarfile(src):
2835*cda5da8dSAndroid Build Coastguard Worker            with TarFile.open(src, 'r:*') as tf:
2836*cda5da8dSAndroid Build Coastguard Worker                tf.list(verbose=args.verbose)
2837*cda5da8dSAndroid Build Coastguard Worker        else:
2838*cda5da8dSAndroid Build Coastguard Worker            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
2839*cda5da8dSAndroid Build Coastguard Worker
2840*cda5da8dSAndroid Build Coastguard Worker    elif args.extract is not None:
2841*cda5da8dSAndroid Build Coastguard Worker        if len(args.extract) == 1:
2842*cda5da8dSAndroid Build Coastguard Worker            src = args.extract[0]
2843*cda5da8dSAndroid Build Coastguard Worker            curdir = os.curdir
2844*cda5da8dSAndroid Build Coastguard Worker        elif len(args.extract) == 2:
2845*cda5da8dSAndroid Build Coastguard Worker            src, curdir = args.extract
2846*cda5da8dSAndroid Build Coastguard Worker        else:
2847*cda5da8dSAndroid Build Coastguard Worker            parser.exit(1, parser.format_help())
2848*cda5da8dSAndroid Build Coastguard Worker
2849*cda5da8dSAndroid Build Coastguard Worker        if is_tarfile(src):
2850*cda5da8dSAndroid Build Coastguard Worker            with TarFile.open(src, 'r:*') as tf:
2851*cda5da8dSAndroid Build Coastguard Worker                tf.extractall(path=curdir, filter=args.filter)
2852*cda5da8dSAndroid Build Coastguard Worker            if args.verbose:
2853*cda5da8dSAndroid Build Coastguard Worker                if curdir == '.':
2854*cda5da8dSAndroid Build Coastguard Worker                    msg = '{!r} file is extracted.'.format(src)
2855*cda5da8dSAndroid Build Coastguard Worker                else:
2856*cda5da8dSAndroid Build Coastguard Worker                    msg = ('{!r} file is extracted '
2857*cda5da8dSAndroid Build Coastguard Worker                           'into {!r} directory.').format(src, curdir)
2858*cda5da8dSAndroid Build Coastguard Worker                print(msg)
2859*cda5da8dSAndroid Build Coastguard Worker        else:
2860*cda5da8dSAndroid Build Coastguard Worker            parser.exit(1, '{!r} is not a tar archive.\n'.format(src))
2861*cda5da8dSAndroid Build Coastguard Worker
2862*cda5da8dSAndroid Build Coastguard Worker    elif args.create is not None:
2863*cda5da8dSAndroid Build Coastguard Worker        tar_name = args.create.pop(0)
2864*cda5da8dSAndroid Build Coastguard Worker        _, ext = os.path.splitext(tar_name)
2865*cda5da8dSAndroid Build Coastguard Worker        compressions = {
2866*cda5da8dSAndroid Build Coastguard Worker            # gz
2867*cda5da8dSAndroid Build Coastguard Worker            '.gz': 'gz',
2868*cda5da8dSAndroid Build Coastguard Worker            '.tgz': 'gz',
2869*cda5da8dSAndroid Build Coastguard Worker            # xz
2870*cda5da8dSAndroid Build Coastguard Worker            '.xz': 'xz',
2871*cda5da8dSAndroid Build Coastguard Worker            '.txz': 'xz',
2872*cda5da8dSAndroid Build Coastguard Worker            # bz2
2873*cda5da8dSAndroid Build Coastguard Worker            '.bz2': 'bz2',
2874*cda5da8dSAndroid Build Coastguard Worker            '.tbz': 'bz2',
2875*cda5da8dSAndroid Build Coastguard Worker            '.tbz2': 'bz2',
2876*cda5da8dSAndroid Build Coastguard Worker            '.tb2': 'bz2',
2877*cda5da8dSAndroid Build Coastguard Worker        }
2878*cda5da8dSAndroid Build Coastguard Worker        tar_mode = 'w:' + compressions[ext] if ext in compressions else 'w'
2879*cda5da8dSAndroid Build Coastguard Worker        tar_files = args.create
2880*cda5da8dSAndroid Build Coastguard Worker
2881*cda5da8dSAndroid Build Coastguard Worker        with TarFile.open(tar_name, tar_mode) as tf:
2882*cda5da8dSAndroid Build Coastguard Worker            for file_name in tar_files:
2883*cda5da8dSAndroid Build Coastguard Worker                tf.add(file_name)
2884*cda5da8dSAndroid Build Coastguard Worker
2885*cda5da8dSAndroid Build Coastguard Worker        if args.verbose:
2886*cda5da8dSAndroid Build Coastguard Worker            print('{!r} file created.'.format(tar_name))
2887*cda5da8dSAndroid Build Coastguard Worker
2888*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__':
2889*cda5da8dSAndroid Build Coastguard Worker    main()
2890