Coverage for diffoscope/comparators/iso9660.py: 62%

52 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-07 13:38 +0000

1# 

2# diffoscope: in-depth comparison of files, archives, and directories 

3# 

4# Copyright © 2015 Jérémy Bobbio <lunar@debian.org> 

5# Copyright © 2015-2020 Chris Lamb <lamby@debian.org> 

6# 

7# diffoscope is free software: you can redistribute it and/or modify 

8# it under the terms of the GNU General Public License as published by 

9# the Free Software Foundation, either version 3 of the License, or 

10# (at your option) any later version. 

11# 

12# diffoscope is distributed in the hope that it will be useful, 

13# but WITHOUT ANY WARRANTY; without even the implied warranty of 

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

15# GNU General Public License for more details. 

16# 

17# You should have received a copy of the GNU General Public License 

18# along with diffoscope. If not, see <https://www.gnu.org/licenses/>. 

19 

20import re 

21import subprocess 

22 

23from diffoscope.tools import tool_required 

24from diffoscope.difference import Difference 

25 

26from .utils.file import File 

27from .utils.command import Command, our_check_output 

28from .utils.libarchive import LibarchiveContainerWithFilelist 

29 

30 

31@tool_required("isoinfo") 

32def get_iso9660_names(path): 

33 return ( 

34 our_check_output( 

35 ( 

36 "isoinfo", 

37 "-R", # Always use RockRidge for names 

38 "-f", 

39 "-i", 

40 path, 

41 ), 

42 ) 

43 .strip() 

44 .split("\n") 

45 ) 

46 

47 

48class ISO9660PVD(Command): 

49 @tool_required("isoinfo") 

50 def cmdline(self): 

51 return ["isoinfo", "-d", "-i", self.path] 

52 

53 

54class ISO9660Listing(Command): 

55 def __init__(self, path, extension=None, *args, **kwargs): 

56 self._extension = extension 

57 super().__init__(path, *args, **kwargs) 

58 

59 @tool_required("isoinfo") 

60 def cmdline(self): 

61 cmd = ["isoinfo", "-l", "-i", self.path] 

62 

63 if self._extension == "joliet": 

64 cmd.extend(["-J", "-j", "iso8859-15"]) 

65 elif self._extension == "rockridge": 

66 cmd.extend(["-R"]) 

67 

68 return cmd 

69 

70 def filter(self, line): 

71 if self._extension == "joliet": 

72 return line.decode("iso-8859-15").encode("utf-8") 

73 return line 

74 

75 

76class Iso9660File(File): 

77 DESCRIPTION = "ISO 9660 CD images" 

78 CONTAINER_CLASSES = [LibarchiveContainerWithFilelist] 

79 FILE_TYPE_RE = re.compile(r"\bISO 9660\b") 

80 

81 @classmethod 

82 def recognizes(cls, file): 

83 if file.magic_file_type and cls.FILE_TYPE_RE.search( 

84 file.magic_file_type 

85 ): 

86 return True 

87 

88 # Sometimes CDs put things like MBRs at the front which is an expected 

89 # part of the ISO9660 standard, but file(1)/libmagic doesn't detect 

90 # this. <https://en.wikipedia.org/wiki/ISO_9660#Specifications>. 

91 with open(file.path, "rb") as f: 

92 f.seek(32769) 

93 return f.read(5) == b"CD001" 

94 

95 return False 

96 

97 def compare_details(self, other, source=None): 

98 differences = [] 

99 

100 for klass in (ISO9660PVD, ISO9660Listing): 

101 differences.append( 

102 Difference.from_operation(klass, self.path, other.path) 

103 ) 

104 

105 for x in ("joliet", "rockridge"): 

106 try: 

107 differences.append( 

108 Difference.from_operation( 

109 ISO9660Listing, 

110 self.path, 

111 other.path, 

112 operation_args=(x,), 

113 ) 

114 ) 

115 except subprocess.CalledProcessError: 

116 # Probably no joliet or rockridge data 

117 pass 

118 

119 return differences