Skip to content

Commit c129cb9

Browse files
committed
Small update
1 parent 06f758c commit c129cb9

1 file changed

Lines changed: 278 additions & 0 deletions

File tree

pyarchivefile/pyarchivefile.py

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import hashlib
3838
import inspect
3939
import tempfile
40+
import libarchive
4041
import configparser
4142
from io import open, StringIO, BytesIO
4243
from decimal import Decimal, ROUND_HALF_UP
@@ -6535,6 +6536,283 @@ def AppendFilesWithContentFromTarFile(infile, fp, extradata=[], jsondata={}, com
65356536
pass
65366537
return fp
65376538

6539+
6540+
def _is_pathlike(x):
6541+
return isinstance(x, (str, bytes, os.PathLike))
6542+
6543+
6544+
def open_archive_reader(infile):
6545+
"""
6546+
Returns a context manager that yields a libarchive reader.
6547+
6548+
Supports:
6549+
- path-like: libarchive.file_reader(path)
6550+
- file object with fileno(): libarchive.fd_reader(fd)
6551+
- bytes / bytearray / memoryview / io.BytesIO: libarchive.memory_reader(data)
6552+
(falls back to a temp file if memory_reader isn't available)
6553+
"""
6554+
# 1) Path
6555+
if _is_pathlike(infile):
6556+
return libarchive.file_reader(infile)
6557+
6558+
# 2) BytesIO or any bytes-like object
6559+
data = None
6560+
if isinstance(infile, io.BytesIO):
6561+
data = infile.getvalue()
6562+
elif isinstance(infile, (bytes, bytearray, memoryview)):
6563+
data = bytes(infile)
6564+
6565+
if data is not None:
6566+
# Preferred: in-memory reader (if available)
6567+
mem_reader = getattr(libarchive, "memory_reader", None)
6568+
if callable(mem_reader):
6569+
return mem_reader(data)
6570+
6571+
# Fallback: spill to temp file (still works everywhere)
6572+
# Note: caller doesn't need to manage cleanup; NamedTemporaryFile is a context manager,
6573+
# but libarchive.file_reader expects a filename. We'll implement a tiny CM wrapper.
6574+
class _TempFileReaderCM:
6575+
def __init__(self, blob):
6576+
self._blob = blob
6577+
self._tmp = None
6578+
self._cm = None
6579+
self._archive = None
6580+
6581+
def __enter__(self):
6582+
self._tmp = tempfile.NamedTemporaryFile(delete=False)
6583+
try:
6584+
self._tmp.write(self._blob)
6585+
self._tmp.flush()
6586+
self._tmp.close()
6587+
self._cm = libarchive.file_reader(self._tmp.name)
6588+
self._archive = self._cm.__enter__()
6589+
return self._archive
6590+
except Exception:
6591+
self.__exit__(None, None, None)
6592+
raise
6593+
6594+
def __exit__(self, exc_type, exc, tb):
6595+
try:
6596+
if self._cm is not None:
6597+
self._cm.__exit__(exc_type, exc, tb)
6598+
finally:
6599+
if self._tmp is not None:
6600+
try:
6601+
os.unlink(self._tmp.name)
6602+
except OSError:
6603+
pass
6604+
6605+
return _TempFileReaderCM(data)
6606+
6607+
# 3) File object with fileno()
6608+
if hasattr(infile, "read") and hasattr(infile, "fileno"):
6609+
return libarchive.fd_reader(infile.fileno())
6610+
6611+
raise TypeError(f"Unsupported infile type: {type(infile)!r}")
6612+
6613+
6614+
def AppendFilesWithContentFromBSDTarFileToList(infile, extradata=[], jsondata={}, contentasfile=False, compression="auto", compresswholefile=True, compressionlevel=None, compressionuselist=compressionlistalt, checksumtype=["md5", "md5", "md5"], formatspecs=__file_format_dict__, saltkey=None, verbose=False):
6615+
curinode = 0
6616+
curfid = 0
6617+
inodelist = []
6618+
inodetofile = {}
6619+
filetoinode = {}
6620+
inodetoforminode = {}
6621+
if(isinstance(infile, (list, tuple, ))):
6622+
infile = infile[0]
6623+
if(infile == "-"):
6624+
infile = MkTempFile()
6625+
shutil.copyfileobj(PY_STDIN_BUF, infile, length=__filebuff_size__)
6626+
infile.seek(0, 0)
6627+
if(not infile):
6628+
return False
6629+
infile.seek(0, 0)
6630+
elif(re.findall(__download_proto_support__, infile) and pywwwget):
6631+
infile = download_file_from_internet_file(infile)
6632+
infile.seek(0, 0)
6633+
if(not infile):
6634+
return False
6635+
infile.seek(0, 0)
6636+
elif(hasattr(infile, "read") or hasattr(infile, "write")):
6637+
try:
6638+
if(not tarfile.is_tarfile(infile)):
6639+
return False
6640+
except AttributeError:
6641+
if(not TarFileCheck(infile)):
6642+
return False
6643+
elif(not os.path.exists(infile) or not os.path.isfile(infile)):
6644+
return False
6645+
tmpoutlist = []
6646+
with open_archive_reader(infile) as archive:
6647+
for member in archive:
6648+
fencoding = "UTF-8"
6649+
fname = member.pathname
6650+
if(verbose):
6651+
VerbosePrintOut(fname)
6652+
fpremode = member.mode
6653+
ffullmode = member.mode
6654+
flinkcount = 0
6655+
fblksize = format(int(0), 'x').lower()
6656+
fblocks = format(int(0), 'x').lower()
6657+
fflags = format(int(0), 'x').lower()
6658+
ftype = 0
6659+
if(member.isreg or member.isfile):
6660+
ffullmode = member.mode | stat.S_IFREG
6661+
ftype = 0
6662+
elif(member.islnk):
6663+
ffullmode = member.mode | stat.S_IFREG
6664+
ftype = 1
6665+
elif(member.issym):
6666+
ffullmode = member.mode | stat.S_IFLNK
6667+
ftype = 2
6668+
elif(member.ischr):
6669+
ffullmode = member.mode | stat.S_IFCHR
6670+
ftype = 3
6671+
elif(member.isblk):
6672+
ffullmode = member.mode | stat.S_IFBLK
6673+
ftype = 4
6674+
elif(member.isdir):
6675+
ffullmode = member.mode | stat.S_IFDIR
6676+
ftype = 5
6677+
elif(member.isfifo):
6678+
ffullmode = member.mode | stat.S_IFIFO
6679+
ftype = 6
6680+
elif(hasattr(member, "issparse") and member.issparse):
6681+
ffullmode = member.mode | stat.S_IFREG
6682+
ftype = 12
6683+
elif(member.isdev()):
6684+
ffullmode = member.mode
6685+
ftype = 14
6686+
else:
6687+
ffullmode = member.mode | stat.S_IFREG
6688+
ftype = 0
6689+
flinkname = ""
6690+
fcurfid = format(int(curfid), 'x').lower()
6691+
fcurinode = format(int(curfid), 'x').lower()
6692+
curfid = curfid + 1
6693+
if(ftype == 2):
6694+
flinkname = member.linkname
6695+
fdev = format(int("0"), 'x').lower()
6696+
frdev = format(int(member.rdev), 'x').lower()
6697+
# Types that should be considered zero-length in the archive context:
6698+
zero_length_types = {1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 13}
6699+
# Types that have actual data to read:
6700+
data_types = {0, 7}
6701+
sparse_types = {12}
6702+
if ftype in zero_length_types:
6703+
fsize = format(int("0"), 'x').lower()
6704+
elif ftype in data_types:
6705+
fsize = format(int(member.size), 'x').lower()
6706+
else:
6707+
fsize = format(int(member.size), 'x').lower()
6708+
if(hasattr(member, "atime") and member.atime is not None):
6709+
fatime = format(int(to_ns(member.atime)), 'x').lower()
6710+
else:
6711+
fatime = format(int(to_ns(member.mtime)), 'x').lower()
6712+
fmtime = format(int(to_ns(member.mtime)), 'x').lower()
6713+
if(hasattr(member, "ctime") and member.ctime is not None):
6714+
fctime = format(int(to_ns(member.ctime)), 'x').lower()
6715+
else:
6716+
fctime = format(int(to_ns(member.mtime)), 'x').lower()
6717+
if(hasattr(member, "birthtime") and member.birthtime is not None):
6718+
fbtime = format(int(to_ns(member.birthtime)), 'x').lower()
6719+
else:
6720+
fbtime = format(int(to_ns(member.mtime)), 'x').lower()
6721+
fmode = format(int(ffullmode), 'x').lower()
6722+
fchmode = format(int(stat.S_IMODE(ffullmode)), 'x').lower()
6723+
ftypemod = format(int(stat.S_IFMT(ffullmode)), 'x').lower()
6724+
fuid = format(int(member.uid), 'x').lower()
6725+
fgid = format(int(member.gid), 'x').lower()
6726+
funame = member.uname
6727+
fgname = member.gname
6728+
flinkcount = format(int(flinkcount), 'x').lower()
6729+
fwinattributes = format(int(0), 'x').lower()
6730+
fcompression = ""
6731+
fcsize = format(int(0), 'x').lower()
6732+
fcontents = MkTempFile()
6733+
fcencoding = "UTF-8"
6734+
curcompression = "none"
6735+
if ftype in data_types:
6736+
for block in member.get_blocks():
6737+
fcontents.write(block)
6738+
typechecktest = CheckCompressionType(fcontents, filestart=0, closefp=False)
6739+
fcontents.seek(0, 0)
6740+
if(typechecktest is not False):
6741+
typechecktest = GetBinaryFileType(fcontents, filestart=0, closefp=False)
6742+
fcontents.seek(0, 0)
6743+
fcencoding = GetFileEncoding(fcontents, 0, False)[0]
6744+
if(typechecktest is False and not compresswholefile):
6745+
fcontents.seek(0, 2)
6746+
ucfsize = fcontents.tell()
6747+
fcontents.seek(0, 0)
6748+
if(compression == "auto"):
6749+
ilsize = len(compressionuselist)
6750+
ilmin = 0
6751+
ilcsize = []
6752+
while(ilmin < ilsize):
6753+
cfcontents = MkTempFile()
6754+
fcontents.seek(0, 0)
6755+
shutil.copyfileobj(fcontents, cfcontents, length=__filebuff_size__)
6756+
fcontents.seek(0, 0)
6757+
cfcontents.seek(0, 0)
6758+
cfcontents = CompressOpenFileAlt(
6759+
cfcontents, compressionuselist[ilmin], compressionlevel, compressionuselist, formatspecs)
6760+
if(cfcontents):
6761+
cfcontents.seek(0, 2)
6762+
ilcsize.append(cfcontents.tell())
6763+
cfcontents.close()
6764+
else:
6765+
ilcsize.append(float("inf"))
6766+
ilmin = ilmin + 1
6767+
ilcmin = ilcsize.index(min(ilcsize))
6768+
curcompression = compressionuselist[ilcmin]
6769+
fcontents.seek(0, 0)
6770+
cfcontents = MkTempFile()
6771+
shutil.copyfileobj(fcontents, cfcontents, length=__filebuff_size__)
6772+
cfcontents.seek(0, 0)
6773+
cfcontents = CompressOpenFileAlt(
6774+
cfcontents, curcompression, compressionlevel, compressionuselist, formatspecs)
6775+
cfcontents.seek(0, 2)
6776+
cfsize = cfcontents.tell()
6777+
if(ucfsize > cfsize):
6778+
fcsize = format(int(cfsize), 'x').lower()
6779+
fcompression = curcompression
6780+
fcontents.close()
6781+
fcontents = cfcontents
6782+
if(fcompression == "none"):
6783+
fcompression = ""
6784+
fcontents.seek(0, 0)
6785+
if(not contentasfile):
6786+
fcontents = fcontents.read()
6787+
ftypehex = format(ftype, 'x').lower()
6788+
tmpoutlist.append({'fheaders': [ftypehex, fencoding, fcencoding, fname, flinkname, fsize, fblksize, fblocks, fflags, fatime, fmtime, fctime, fbtime, fmode, fwinattributes, fcompression,
6789+
fcsize, fuid, funame, fgid, fgname, fcurfid, fcurinode, flinkcount, fdev, frdev, "+"+str(len(formatspecs['format_delimiter']))], 'fextradata': extradata, 'fjsoncontent': jsondata, 'fcontents': fcontents, 'fjsonchecksumtype': checksumtype[2], 'fheaderchecksumtype': checksumtype[0], 'fcontentchecksumtype': checksumtype[1]})
6790+
return tmpoutlist
6791+
6792+
def AppendFilesWithContentFromBSDTarFile(infile, fp, extradata=[], jsondata={}, compression="auto", compresswholefile=True, compressionlevel=None, compressionuselist=compressionlistalt, checksumtype=["md5", "md5", "md5", "md5", "md5"], formatspecs=__file_format_dict__, saltkey=None, verbose=False):
6793+
if(not hasattr(fp, "write")):
6794+
return False
6795+
GetDirList = AppendFilesWithContentFromBSDTarFileToList(infile, extradata, jsondata, False, compression, compresswholefile, compressionlevel, compressionuselist, [checksumtype[2], checksumtype[3], checksumtype[3]], formatspecs, saltkey, verbose)
6796+
numfiles = int(len(GetDirList))
6797+
fnumfiles = format(numfiles, 'x').lower()
6798+
AppendFileHeader(fp, numfiles, "UTF-8", [], {}, [checksumtype[0], checksumtype[1]], formatspecs, saltkey)
6799+
try:
6800+
fp.flush()
6801+
if(hasattr(os, "sync")):
6802+
os.fsync(fp.fileno())
6803+
except (io.UnsupportedOperation, AttributeError, OSError):
6804+
pass
6805+
for curfname in GetDirList:
6806+
tmpoutlist = curfname['fheaders']
6807+
AppendFileHeaderWithContent(fp, tmpoutlist, curfname['fextradata'], curfname['fjsoncontent'], curfname['fcontents'], [curfname['fheaderchecksumtype'], curfname['fcontentchecksumtype'], curfname['fjsonchecksumtype']], formatspecs, saltkey)
6808+
try:
6809+
fp.flush()
6810+
if(hasattr(os, "sync")):
6811+
os.fsync(fp.fileno())
6812+
except (io.UnsupportedOperation, AttributeError, OSError):
6813+
pass
6814+
return fp
6815+
65386816
def AppendFilesWithContentFromZipFileToList(infile, extradata=[], jsondata={}, contentasfile=False, compression="auto", compresswholefile=True, compressionlevel=None, compressionuselist=compressionlistalt, checksumtype=["md5", "md5", "md5", "md5", "md5"], formatspecs=__file_format_dict__, saltkey=None, verbose=False):
65396817
curinode = 0
65406818
curfid = 0

0 commit comments

Comments
 (0)