import_tar.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #
  2. # Copyright (C) 2008 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import bz2
  16. import stat
  17. import tarfile
  18. import zlib
  19. import StringIO
  20. from import_ext import ImportExternal
  21. from error import ImportError
  22. class ImportTar(ImportExternal):
  23. """Streams a (optionally compressed) tar file from the network
  24. directly into a Project's Git repository.
  25. """
  26. @classmethod
  27. def CanAccept(cls, url):
  28. """Can this importer read and unpack the data stored at url?
  29. """
  30. if url.endswith('.tar.gz') or url.endswith('.tgz'):
  31. return True
  32. if url.endswith('.tar.bz2'):
  33. return True
  34. if url.endswith('.tar'):
  35. return True
  36. return False
  37. def _UnpackFiles(self):
  38. url_fd, url = self._OpenUrl()
  39. try:
  40. if url.endswith('.tar.gz') or url.endswith('.tgz'):
  41. tar_fd = _Gzip(url_fd)
  42. elif url.endswith('.tar.bz2'):
  43. tar_fd = _Bzip2(url_fd)
  44. elif url.endswith('.tar'):
  45. tar_fd = _Raw(url_fd)
  46. else:
  47. raise ImportError('non-tar file extension: %s' % url)
  48. try:
  49. tar = tarfile.TarFile(name = url,
  50. mode = 'r',
  51. fileobj = tar_fd)
  52. try:
  53. for entry in tar:
  54. mode = entry.mode
  55. if (mode & 0170000) == 0:
  56. if entry.isdir():
  57. mode |= stat.S_IFDIR
  58. elif entry.isfile() or entry.islnk(): # hard links as files
  59. mode |= stat.S_IFREG
  60. elif entry.issym():
  61. mode |= stat.S_IFLNK
  62. if stat.S_ISLNK(mode): # symlink
  63. data_fd = StringIO.StringIO(entry.linkname)
  64. data_sz = len(entry.linkname)
  65. elif stat.S_ISDIR(mode): # directory
  66. data_fd = StringIO.StringIO('')
  67. data_sz = 0
  68. else:
  69. data_fd = tar.extractfile(entry)
  70. data_sz = entry.size
  71. self._UnpackOneFile(mode, data_sz, entry.name, data_fd)
  72. finally:
  73. tar.close()
  74. finally:
  75. tar_fd.close()
  76. finally:
  77. url_fd.close()
  78. class _DecompressStream(object):
  79. """file like object to decompress a tar stream
  80. """
  81. def __init__(self, fd):
  82. self._fd = fd
  83. self._pos = 0
  84. self._buf = None
  85. def tell(self):
  86. return self._pos
  87. def seek(self, offset):
  88. d = offset - self._pos
  89. if d > 0:
  90. self.read(d)
  91. elif d == 0:
  92. pass
  93. else:
  94. raise NotImplementedError, 'seek backwards'
  95. def close(self):
  96. self._fd = None
  97. def read(self, size = -1):
  98. if not self._fd:
  99. raise EOFError, 'Reached EOF'
  100. r = []
  101. try:
  102. if size >= 0:
  103. self._ReadChunk(r, size)
  104. else:
  105. while True:
  106. self._ReadChunk(r, 2048)
  107. except EOFError:
  108. pass
  109. if len(r) == 1:
  110. r = r[0]
  111. else:
  112. r = ''.join(r)
  113. self._pos += len(r)
  114. return r
  115. def _ReadChunk(self, r, size):
  116. b = self._buf
  117. try:
  118. while size > 0:
  119. if b is None or len(b) == 0:
  120. b = self._Decompress(self._fd.read(2048))
  121. continue
  122. use = min(size, len(b))
  123. r.append(b[:use])
  124. b = b[use:]
  125. size -= use
  126. finally:
  127. self._buf = b
  128. def _Decompress(self, b):
  129. raise NotImplementedError, '_Decompress'
  130. class _Raw(_DecompressStream):
  131. """file like object for an uncompressed stream
  132. """
  133. def __init__(self, fd):
  134. _DecompressStream.__init__(self, fd)
  135. def _Decompress(self, b):
  136. return b
  137. class _Bzip2(_DecompressStream):
  138. """file like object to decompress a .bz2 stream
  139. """
  140. def __init__(self, fd):
  141. _DecompressStream.__init__(self, fd)
  142. self._bz = bz2.BZ2Decompressor()
  143. def _Decompress(self, b):
  144. return self._bz.decompress(b)
  145. _FHCRC, _FEXTRA, _FNAME, _FCOMMENT = 2, 4, 8, 16
  146. class _Gzip(_DecompressStream):
  147. """file like object to decompress a .gz stream
  148. """
  149. def __init__(self, fd):
  150. _DecompressStream.__init__(self, fd)
  151. self._z = zlib.decompressobj(-zlib.MAX_WBITS)
  152. magic = fd.read(2)
  153. if magic != '\037\213':
  154. raise IOError, 'Not a gzipped file'
  155. method = ord(fd.read(1))
  156. if method != 8:
  157. raise IOError, 'Unknown compression method'
  158. flag = ord(fd.read(1))
  159. fd.read(6)
  160. if flag & _FEXTRA:
  161. xlen = ord(fd.read(1))
  162. xlen += 256 * ord(fd.read(1))
  163. fd.read(xlen)
  164. if flag & _FNAME:
  165. while fd.read(1) != '\0':
  166. pass
  167. if flag & _FCOMMENT:
  168. while fd.read(1) != '\0':
  169. pass
  170. if flag & _FHCRC:
  171. fd.read(2)
  172. def _Decompress(self, b):
  173. return self._z.decompress(b)