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