kopia lustrzana https://github.com/micropython/micropython-lib
122 wiersze
3.9 KiB
Python
122 wiersze
3.9 KiB
Python
"""Additions to the TarFile class to support creating and appending tar files.
|
|
|
|
The methods defined below in are injected into the TarFile class in the
|
|
tarfile package.
|
|
"""
|
|
|
|
import uctypes
|
|
import os
|
|
|
|
# Extended subset of tar header fields including the ones we'll write.
|
|
# http://www.gnu.org/software/tar/manual/html_node/Standard.html
|
|
_TAR_HEADER = {
|
|
"name": (uctypes.ARRAY | 0, uctypes.UINT8 | 100),
|
|
"mode": (uctypes.ARRAY | 100, uctypes.UINT8 | 8),
|
|
"uid": (uctypes.ARRAY | 108, uctypes.UINT8 | 8),
|
|
"gid": (uctypes.ARRAY | 116, uctypes.UINT8 | 8),
|
|
"size": (uctypes.ARRAY | 124, uctypes.UINT8 | 12),
|
|
"mtime": (uctypes.ARRAY | 136, uctypes.UINT8 | 12),
|
|
"chksum": (uctypes.ARRAY | 148, uctypes.UINT8 | 8),
|
|
"typeflag": (uctypes.ARRAY | 156, uctypes.UINT8 | 1),
|
|
}
|
|
|
|
|
|
_NUL = const(b"\0") # the null character
|
|
_BLOCKSIZE = const(512) # length of processing blocks
|
|
_RECORDSIZE = const(_BLOCKSIZE * 20) # length of records
|
|
|
|
|
|
def _open_write(self, name, mode, fileobj):
|
|
if mode == "w":
|
|
if not fileobj:
|
|
self.f = open(name, "wb")
|
|
else:
|
|
self.f = fileobj
|
|
elif mode == "a":
|
|
if not fileobj:
|
|
self.f = open(name, "r+b")
|
|
else:
|
|
self.f = fileobj
|
|
# Read through the existing file.
|
|
while self.next():
|
|
pass
|
|
# Position at start of end block.
|
|
self.f.seek(self.offset)
|
|
else:
|
|
raise ValueError("mode " + mode + " not supported.")
|
|
|
|
|
|
def _close_write(self):
|
|
# Must be called to complete writing a tar file.
|
|
if self.mode == "w":
|
|
self.f.write(_NUL * (_BLOCKSIZE * 2))
|
|
self.offset += _BLOCKSIZE * 2
|
|
remainder = self.offset % _RECORDSIZE
|
|
if remainder:
|
|
self.f.write(_NUL * (_RECORDSIZE - remainder))
|
|
|
|
|
|
def addfile(self, tarinfo, fileobj=None):
|
|
# Write the header: 100 bytes of name, 8 bytes of mode in octal...
|
|
buf = bytearray(_BLOCKSIZE)
|
|
name = tarinfo.name
|
|
size = tarinfo.size
|
|
if tarinfo.isdir():
|
|
size = 0
|
|
if not name.endswith("/"):
|
|
name += "/"
|
|
hdr = uctypes.struct(uctypes.addressof(buf), _TAR_HEADER, uctypes.LITTLE_ENDIAN)
|
|
hdr.name[:] = name.encode("utf-8")[:100]
|
|
hdr.mode[:] = b"%07o\0" % ((0o755 if tarinfo.isdir() else 0o644) & 0o7777)
|
|
hdr.uid[:] = b"%07o\0" % tarinfo.uid
|
|
hdr.gid[:] = b"%07o\0" % tarinfo.gid
|
|
hdr.size[:] = b"%011o\0" % size
|
|
hdr.mtime[:] = b"%011o\0" % tarinfo.mtime
|
|
hdr.typeflag[:] = b"5" if tarinfo.isdir() else b"0"
|
|
# Checksum is calculated with checksum field all blanks.
|
|
hdr.chksum[:] = b" "
|
|
# Calculate and insert the actual checksum.
|
|
chksum = sum(buf)
|
|
hdr.chksum[:] = b"%06o\0 " % chksum
|
|
# Emit the header.
|
|
self.f.write(buf)
|
|
self.offset += len(buf)
|
|
|
|
# Copy the file contents, if any.
|
|
if fileobj:
|
|
n_bytes = self.f.write(fileobj.read())
|
|
self.offset += n_bytes
|
|
remains = -n_bytes & (_BLOCKSIZE - 1) # == 0b111111111
|
|
if remains:
|
|
buf = bytearray(remains)
|
|
self.f.write(buf)
|
|
self.offset += len(buf)
|
|
|
|
|
|
def add(self, name, recursive=True):
|
|
from . import TarInfo
|
|
|
|
try:
|
|
stat = os.stat(name)
|
|
res_name = (name + '/') if (stat[0] & 0xf000) == 0x4000 else name
|
|
tarinfo = TarInfo(res_name)
|
|
tarinfo.mode = stat[0]
|
|
tarinfo.uid = stat[4]
|
|
tarinfo.gid = stat[5]
|
|
tarinfo.size = stat[6]
|
|
tarinfo.mtime = stat[8]
|
|
except OSError:
|
|
print("Cannot stat", name, " - skipping.")
|
|
return
|
|
if not (tarinfo.isdir() or tarinfo.isreg()):
|
|
# We only accept directories or regular files.
|
|
print(name, "is not a directory or regular file - skipping.")
|
|
return
|
|
if tarinfo.isdir():
|
|
self.addfile(tarinfo)
|
|
if recursive:
|
|
for f in os.ilistdir(name):
|
|
self.add(name + "/" + f[0], recursive)
|
|
else: # type == REGTYPE
|
|
self.addfile(tarinfo, open(name, "rb"))
|