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

26 statements  

1from typing import Optional 

2 

3from structlog import get_logger 

4 

5from unblob.extractors import Command 

6from unblob.file_utils import Endian 

7 

8from ...models import ( 

9 File, 

10 HandlerDoc, 

11 HandlerType, 

12 HexString, 

13 Reference, 

14 StructHandler, 

15 ValidChunk, 

16) 

17 

18logger = get_logger() 

19 

20 

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

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

23SYSTEM_AREA_SIZE = 0x8000 

24 

25 

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

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

28 

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

30 """ 

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

32 

33 

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

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

36 

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

38 """ 

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

40 

41 

42class ISO9660FSHandler(StructHandler): 

43 NAME = "iso9660" 

44 

45 # 

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

47 # 

48 # Volume descriptor types can be: 

49 # - 0x00 Boot record volume descriptor 

50 # - 0x01 Primary volume descriptor 

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

52 # - 0x03 Volume partition descriptor 

53 # - 0xFF Volume descriptor terminator 

54 

55 PATTERNS = [ 

56 HexString( 

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

58 ) 

59 ] 

60 

61 C_DEFINITIONS = r""" 

62 typedef struct iso9660_dtime_s { 

63 uint8 dt_year; 

64 uint8 dt_month; 

65 uint8 dt_day; 

66 uint8 dt_hour; 

67 uint8 dt_minute; 

68 uint8 dt_second; 

69 int8 dt_gmtoff; 

70 } iso9660_dtime_t; 

71 

72 typedef struct iso9660_ltime_s { 

73 char lt_year[4]; 

74 char lt_month[2]; 

75 char lt_day[2]; 

76 char lt_hour[2]; 

77 char lt_minute[2]; 

78 char lt_second[2]; 

79 char lt_hsecond[2]; 

80 int8 lt_gmtoff; 

81 } iso9660_ltime_t; 

82 

83 typedef struct iso9660_dir_s { 

84 uint8 length; 

85 uint8 xa_length; 

86 uint64 extent; 

87 uint64 size; 

88 iso9660_dtime_t recording_time; 

89 uint8 file_flags; 

90 uint8 file_unit_size; 

91 uint8 interleave_gap; 

92 uint32 volume_sequence_number; 

93 union { 

94 uint8 len; 

95 char str[1]; 

96 } filename; 

97 } iso9660_dir_t; 

98 

99 typedef struct iso9660_pvd_s { 

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

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

102 */ 

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

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

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

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

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

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

109 sectors */ 

110 uint8 volume_space_size[8]; 

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

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

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

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

115 } iso9660_pvd_t; 

116 """ 

117 

118 HEADER_STRUCT = "iso9660_pvd_t" 

119 

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

121 

122 DOC = HandlerDoc( 

123 name="ISO 9660", 

124 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.", 

125 handler_type=HandlerType.FILESYSTEM, 

126 vendor=None, 

127 references=[ 

128 Reference( 

129 title="ISO 9660 Specification", 

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

131 ), 

132 Reference( 

133 title="ISO 9660 Wikipedia", 

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

135 ), 

136 ], 

137 limitations=[], 

138 ) 

139 

140 def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]: 

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

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

143 

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

145 # which is the first struct afterward. 

146 real_start_offset = start_offset - SYSTEM_AREA_SIZE 

147 if real_start_offset < 0: 

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

149 return None 

150 

151 return ValidChunk( 

152 start_offset=real_start_offset, 

153 end_offset=real_start_offset + size, 

154 )