upip_utarfile.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import uctypes
  2. # http://www.gnu.org/software/tar/manual/html_node/Standard.html
  3. TAR_HEADER = {
  4. "name": (uctypes.ARRAY | 0, uctypes.UINT8 | 100),
  5. "size": (uctypes.ARRAY | 124, uctypes.UINT8 | 11),
  6. }
  7. DIRTYPE = "dir"
  8. REGTYPE = "file"
  9. def roundup(val, align):
  10. return (val + align - 1) & ~(align - 1)
  11. class FileSection:
  12. def __init__(self, f, content_len, aligned_len):
  13. self.f = f
  14. self.content_len = content_len
  15. self.align = aligned_len - content_len
  16. def read(self, sz=65536):
  17. if self.content_len == 0:
  18. return b""
  19. if sz > self.content_len:
  20. sz = self.content_len
  21. data = self.f.read(sz)
  22. sz = len(data)
  23. self.content_len -= sz
  24. return data
  25. def readinto(self, buf):
  26. if self.content_len == 0:
  27. return 0
  28. if len(buf) > self.content_len:
  29. buf = memoryview(buf)[:self.content_len]
  30. sz = self.f.readinto(buf)
  31. self.content_len -= sz
  32. return sz
  33. def skip(self):
  34. sz = self.content_len + self.align
  35. if sz:
  36. buf = bytearray(16)
  37. while sz:
  38. s = min(sz, 16)
  39. self.f.readinto(buf, s)
  40. sz -= s
  41. class TarInfo:
  42. def __str__(self):
  43. return "TarInfo(%r, %s, %d)" % (self.name, self.type, self.size)
  44. class TarFile:
  45. def __init__(self, name=None, fileobj=None):
  46. if fileobj:
  47. self.f = fileobj
  48. else:
  49. self.f = open(name, "rb")
  50. self.subf = None
  51. def next(self):
  52. if self.subf:
  53. self.subf.skip()
  54. buf = self.f.read(512)
  55. if not buf:
  56. return None
  57. h = uctypes.struct(uctypes.addressof(buf), TAR_HEADER, uctypes.LITTLE_ENDIAN)
  58. # Empty block means end of archive
  59. if h.name[0] == 0:
  60. return None
  61. d = TarInfo()
  62. d.name = str(h.name, "utf-8").rstrip("\0")
  63. d.size = int(bytes(h.size), 8)
  64. d.type = [REGTYPE, DIRTYPE][d.name[-1] == "/"]
  65. self.subf = d.subf = FileSection(self.f, d.size, roundup(d.size, 512))
  66. return d
  67. def __iter__(self):
  68. return self
  69. def __next__(self):
  70. v = self.next()
  71. if v is None:
  72. raise StopIteration
  73. return v
  74. def extractfile(self, tarinfo):
  75. return tarinfo.subf