PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/hachoir_parser/archive/tar.py

http://github.com/midgetspy/Sick-Beard
Python | 124 lines | 106 code | 9 blank | 9 comment | 2 complexity | 32b5d7f0d67c3f06a931902d80905925 MD5 | raw file
Possible License(s): Unlicense
  1. """
  2. Tar archive parser.
  3. Author: Victor Stinner
  4. """
  5. from lib.hachoir_parser import Parser
  6. from lib.hachoir_core.field import (FieldSet,
  7. Enum, UInt8, SubFile, String, NullBytes)
  8. from lib.hachoir_core.tools import humanFilesize, paddingSize, timestampUNIX
  9. from lib.hachoir_core.endian import BIG_ENDIAN
  10. import re
  11. class FileEntry(FieldSet):
  12. type_name = {
  13. # 48 is "0", 49 is "1", ...
  14. 0: u"Normal disk file (old format)",
  15. 48: u"Normal disk file",
  16. 49: u"Link to previously dumped file",
  17. 50: u"Symbolic link",
  18. 51: u"Character special file",
  19. 52: u"Block special file",
  20. 53: u"Directory",
  21. 54: u"FIFO special file",
  22. 55: u"Contiguous file"
  23. }
  24. def getOctal(self, name):
  25. return self.octal2int(self[name].value)
  26. def getDatetime(self):
  27. """
  28. Create modification date as Unicode string, may raise ValueError.
  29. """
  30. timestamp = self.getOctal("mtime")
  31. return timestampUNIX(timestamp)
  32. def createFields(self):
  33. yield String(self, "name", 100, "Name", strip="\0", charset="ISO-8859-1")
  34. yield String(self, "mode", 8, "Mode", strip=" \0", charset="ASCII")
  35. yield String(self, "uid", 8, "User ID", strip=" \0", charset="ASCII")
  36. yield String(self, "gid", 8, "Group ID", strip=" \0", charset="ASCII")
  37. yield String(self, "size", 12, "Size", strip=" \0", charset="ASCII")
  38. yield String(self, "mtime", 12, "Modification time", strip=" \0", charset="ASCII")
  39. yield String(self, "check_sum", 8, "Check sum", strip=" \0", charset="ASCII")
  40. yield Enum(UInt8(self, "type", "Type"), self.type_name)
  41. yield String(self, "lname", 100, "Link name", strip=" \0", charset="ISO-8859-1")
  42. yield String(self, "magic", 8, "Magic", strip=" \0", charset="ASCII")
  43. yield String(self, "uname", 32, "User name", strip=" \0", charset="ISO-8859-1")
  44. yield String(self, "gname", 32, "Group name", strip=" \0", charset="ISO-8859-1")
  45. yield String(self, "devmajor", 8, "Dev major", strip=" \0", charset="ASCII")
  46. yield String(self, "devminor", 8, "Dev minor", strip=" \0", charset="ASCII")
  47. yield NullBytes(self, "padding", 167, "Padding (zero)")
  48. filesize = self.getOctal("size")
  49. if filesize:
  50. yield SubFile(self, "content", filesize, filename=self["name"].value)
  51. size = paddingSize(self.current_size//8, 512)
  52. if size:
  53. yield NullBytes(self, "padding_end", size, "Padding (512 align)")
  54. def convertOctal(self, chunk):
  55. return self.octal2int(chunk.value)
  56. def isEmpty(self):
  57. return self["name"].value == ""
  58. def octal2int(self, text):
  59. try:
  60. return int(text, 8)
  61. except ValueError:
  62. return 0
  63. def createDescription(self):
  64. if self.isEmpty():
  65. desc = "(terminator, empty header)"
  66. else:
  67. filename = self["name"].value
  68. filesize = humanFilesize(self.getOctal("size"))
  69. desc = "(%s: %s, %s)" % \
  70. (filename, self["type"].display, filesize)
  71. return "Tar File " + desc
  72. class TarFile(Parser):
  73. endian = BIG_ENDIAN
  74. PARSER_TAGS = {
  75. "id": "tar",
  76. "category": "archive",
  77. "file_ext": ("tar",),
  78. "mime": (u"application/x-tar", u"application/x-gtar"),
  79. "min_size": 512*8,
  80. "magic": (("ustar \0", 257*8),),
  81. "subfile": "skip",
  82. "description": "TAR archive",
  83. }
  84. _sign = re.compile("ustar *\0|[ \0]*$")
  85. def validate(self):
  86. if not self._sign.match(self.stream.readBytes(257*8, 8)):
  87. return "Invalid magic number"
  88. if self[0].name == "terminator":
  89. return "Don't contain any file"
  90. try:
  91. int(self["file[0]/uid"].value, 8)
  92. int(self["file[0]/gid"].value, 8)
  93. int(self["file[0]/size"].value, 8)
  94. except ValueError:
  95. return "Invalid file size"
  96. return True
  97. def createFields(self):
  98. while not self.eof:
  99. field = FileEntry(self, "file[]")
  100. if field.isEmpty():
  101. yield NullBytes(self, "terminator", 512)
  102. break
  103. yield field
  104. if self.current_size < self._size:
  105. yield self.seekBit(self._size, "end")
  106. def createContentSize(self):
  107. return self["terminator"].address + self["terminator"].size