Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/filesystem/iso9660.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

25 statements  

1from structlog import get_logger 

2 

3from unblob.extractors import Command 

4from unblob.file_utils import Endian 

5 

6from ...models import ( 

7 File, 

8 HandlerDoc, 

9 HandlerType, 

10 HexString, 

11 Reference, 

12 StructHandler, 

13 ValidChunk, 

14) 

15 

16logger = get_logger() 

17 

18 

19# The system area, the first 32,768 data bytes of the disc (16 sectors of 2,048 bytes each), 

20# is unused by ISO 9660 and therefore available for other uses. 

21SYSTEM_AREA_SIZE = 0x8000 

22 

23 

24def from_733(u: bytes) -> int: 

25 """Convert from ISO 9660 7.3.3 format to uint32_t. 

26 

27 Return the little-endian part always, to handle non-specs-compliant images. 

28 """ 

29 return u[0] | (u[1] << 8) | (u[2] << 16) | (u[3] << 24) 

30 

31 

32def from_723(u: bytes) -> int: 

33 """Convert from ISO 9660 7.2.3 format to uint16_t. 

34 

35 Return the little-endian part always, to handle non-specs-compliant images. 

36 """ 

37 return u[0] | (u[1] << 8) 

38 

39 

40class ISO9660FSHandler(StructHandler): 

41 NAME = "iso9660" 

42 

43 # 

44 # Match on volume descriptor type, followed by ISO_STANDARD_ID, which corresponds to the beginning of a volume descriptor. 

45 # 

46 # Volume descriptor types can be: 

47 # - 0x00 Boot record volume descriptor 

48 # - 0x01 Primary volume descriptor 

49 # - 0x02 Supplementary volume descriptor, or enhanced volume descriptor 

50 # - 0x03 Volume partition descriptor 

51 # - 0xFF Volume descriptor terminator 

52 

53 PATTERNS = [ 

54 HexString( 

55 "( 00 | 01 | 02 | 03 ) 43 44 30 30 31 // vd_type + 'CD001' (ISO_STANDARD_ID within primary volume descriptor)" 

56 ) 

57 ] 

58 

59 C_DEFINITIONS = r""" 

60 typedef struct iso9660_dtime_s { 

61 uint8 dt_year; 

62 uint8 dt_month; 

63 uint8 dt_day; 

64 uint8 dt_hour; 

65 uint8 dt_minute; 

66 uint8 dt_second; 

67 int8 dt_gmtoff; 

68 } iso9660_dtime_t; 

69 

70 typedef struct iso9660_ltime_s { 

71 char lt_year[4]; 

72 char lt_month[2]; 

73 char lt_day[2]; 

74 char lt_hour[2]; 

75 char lt_minute[2]; 

76 char lt_second[2]; 

77 char lt_hsecond[2]; 

78 int8 lt_gmtoff; 

79 } iso9660_ltime_t; 

80 

81 typedef struct iso9660_dir_s { 

82 uint8 length; 

83 uint8 xa_length; 

84 uint64 extent; 

85 uint64 size; 

86 iso9660_dtime_t recording_time; 

87 uint8 file_flags; 

88 uint8 file_unit_size; 

89 uint8 interleave_gap; 

90 uint32 volume_sequence_number; 

91 union { 

92 uint8 len; 

93 char str[1]; 

94 } filename; 

95 } iso9660_dir_t; 

96 

97 typedef struct iso9660_pvd_s { 

98 uint8 type; /**< ISO_VD_PRIMARY - 1 */ 

99 char id[5]; /**< ISO_STANDARD_ID "CD001" 

100 */ 

101 uint8 version; /**< value 1 for ECMA 119 */ 

102 char unused1[1]; /**< unused - value 0 */ 

103 char system_id[32]; /**< each char is an achar */ 

104 char volume_id[32]; /**< each char is a dchar */ 

105 uint8 unused2[8]; /**< unused - value 0 */ 

106 /**uint64 volume_space_size; /**< total number of 

107 sectors */ 

108 uint8 volume_space_size[8]; 

109 char unused3[32]; /**< unused - value 0 */ 

110 uint32 volume_set_size; /**< often 1 */ 

111 uint32 volume_sequence_number; /**< often 1 */ 

112 uint8 logical_block_size[4]; /**< sector size, e.g. 2048 */ 

113 } iso9660_pvd_t; 

114 """ 

115 

116 HEADER_STRUCT = "iso9660_pvd_t" 

117 

118 EXTRACTOR = Command("7z", "x", "-y", "{inpath}", "-o{outdir}") 

119 

120 DOC = HandlerDoc( 

121 name="ISO 9660", 

122 description="ISO 9660 is a file system standard for optical disc media, defining a volume descriptor structure and directory hierarchy. It is widely used for CD-ROMs and supports cross-platform compatibility.", 

123 handler_type=HandlerType.FILESYSTEM, 

124 vendor=None, 

125 references=[ 

126 Reference( 

127 title="ISO 9660 Specification", 

128 url="https://wiki.osdev.org/ISO_9660", 

129 ), 

130 Reference( 

131 title="ISO 9660 Wikipedia", 

132 url="https://en.wikipedia.org/wiki/ISO_9660", 

133 ), 

134 ], 

135 limitations=[], 

136 ) 

137 

138 def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None: 

139 header = self.parse_header(file, Endian.LITTLE) 

140 size = from_733(header.volume_space_size) * from_723(header.logical_block_size) 

141 

142 # We need to subtract the system area given that we matched on volume descriptor, 

143 # which is the first struct afterward. 

144 real_start_offset = start_offset - SYSTEM_AREA_SIZE 

145 if real_start_offset < 0: 

146 logger.warning("Invalid ISO 9660 file", offset=real_start_offset, size=size) 

147 return None 

148 

149 return ValidChunk( 

150 start_offset=real_start_offset, 

151 end_offset=real_start_offset + size, 

152 )