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

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

40 statements  

1from typing import Optional 

2 

3from structlog import get_logger 

4 

5from unblob.file_utils import InvalidInputFormat 

6 

7from ...extractors import Command 

8from ...models import ( 

9 File, 

10 HandlerDoc, 

11 HandlerType, 

12 HexString, 

13 Reference, 

14 StructHandler, 

15 ValidChunk, 

16) 

17 

18logger = get_logger() 

19 

20 

21EXT_BLOCK_SIZE = 0x400 

22MAGIC_OFFSET = 0x438 

23 

24OS_LIST = [ 

25 (0x0, "Linux"), 

26 (0x1, "GNU HURD"), 

27 (0x2, "MASIX"), 

28 (0x3, "FreeBSD"), 

29 ( 

30 0x4, 

31 "Other", 

32 ), # Other "Lites" (BSD4.4-Lite derivatives such as NetBSD, OpenBSD, XNU/Darwin, etc.) 

33] 

34 

35 

36class EXTHandler(StructHandler): 

37 NAME = "extfs" 

38 

39 PATTERNS = [HexString("53 ef ( 00 | 01 | 02 ) 00 ( 00 | 01 | 02 | 03 | 04 ) 00")] 

40 

41 C_DEFINITIONS = r""" 

42 typedef struct ext4_superblock { 

43 char blank[0x400]; // Not a part of the spec. But we expect the magic to be at 0x438. 

44 uint32 s_inodes_count; // Total number of inodes in file system 

45 uint32 s_blocks_count_lo; // Total number of blocks in file system 

46 uint32 s_r_blocks_count_lo; // Number of blocks reserved for superuser (see offset 80) 

47 uint32 s_free_blocks_count_lo; // Total number of unallocated blocks 

48 uint32 s_free_inodes_count; // Total number of unallocated inodes 

49 uint32 s_first_data_block; // Block number of the block containing the superblock 

50 uint32 s_log_block_size; // log2 (block size) - 10 (In other words, the number to shift 1,024 to the left by to obtain the block size) 

51 uint32 s_log_cluster_size; // log2 (fragment size) - 10. (In other words, the number to shift 1,024 to the left by to obtain the fragment size) 

52 uint32 s_blocks_per_group; // Number of blocks in each block group 

53 uint32 s_clusters_per_group; // Number of fragments in each block group 

54 uint32 s_inodes_per_group; // Number of inodes in each block group 

55 uint32 s_mtime; // Last mount time 

56 uint32 s_wtime; // Last written time 

57 uint16 s_mnt_count; // Number of times the volume has been mounted since its last consistency check 

58 uint16 s_max_mnt_count; // Number of mounts allowed before a consistency check must be done 

59 uint16 s_magic; // Ext signature (0xef53), used to help confirm the presence of Ext2 on a volume 

60 uint16 s_state; // File system state (0x1 - clean or 0x2 - has errors) 

61 uint16 s_errors; // What to do when an error is detected (ignore/remount/kernel panic) 

62 uint16 s_minor_rev_level; // Minor portion of version (combine with Major portion below to construct full version field) 

63 uint32 s_lastcheck; // time of last consistency check 

64 uint32 s_checkinterval; // Interval between forced consistency checks 

65 uint32 s_creator_os; // Operating system ID from which the filesystem on this volume was created 

66 uint32 s_rev_level; // Major portion of version (combine with Minor portion above to construct full version field) 

67 uint16 s_def_resuid; // User ID that can use reserved blocks 

68 uint16 s_def_resgid; // Group ID that can use reserved blocks 

69 } ext4_superblock_t; 

70 """ 

71 HEADER_STRUCT = "ext4_superblock_t" 

72 

73 PATTERN_MATCH_OFFSET = -MAGIC_OFFSET 

74 

75 EXTRACTOR = Command("debugfs", "-R", 'rdump / "{outdir}"', "{inpath}") 

76 

77 DOC = HandlerDoc( 

78 name="ExtFS", 

79 description="ExtFS (Ext2/Ext3/Ext4) is a family of journaling file systems commonly used in Linux-based operating systems. It supports features like large file sizes, extended attributes, and journaling for improved reliability.", 

80 handler_type=HandlerType.FILESYSTEM, 

81 vendor=None, 

82 references=[ 

83 Reference( 

84 title="Ext4 Documentation", 

85 url="https://www.kernel.org/doc/html/latest/filesystems/ext4/index.html", 

86 ), 

87 Reference( 

88 title="ExtFS Wikipedia", 

89 url="https://en.wikipedia.org/wiki/Ext4", 

90 ), 

91 ], 

92 limitations=[], 

93 ) 

94 

95 def valid_header(self, header) -> bool: 

96 if header.s_state not in [0x0, 0x1, 0x2]: 

97 logger.debug("ExtFS header state not valid", state=header.s_state) 

98 return False 

99 if header.s_errors not in [0x0, 0x1, 0x2, 0x3]: 

100 logger.debug( 

101 "ExtFS header error handling method value not valid", 

102 errors=header.s_errors, 

103 ) 

104 return False 

105 if header.s_creator_os not in [x[0] for x in OS_LIST]: 

106 logger.debug("Creator OS value not valid.", creator_os=header.s_creator_os) 

107 return False 

108 if header.s_rev_level > 2: 

109 logger.debug( 

110 "ExtFS header major version too high", rev_level=header.s_rev_level 

111 ) 

112 return False 

113 if header.s_log_block_size > 6: 

114 logger.debug( 

115 "ExtFS header s_log_block_size is too large", 

116 s_log_block_size=header.s_log_block_size, 

117 ) 

118 return False 

119 return True 

120 

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

122 header = self.parse_header(file) 

123 end_offset = start_offset + ( 

124 header.s_blocks_count_lo * (EXT_BLOCK_SIZE << header.s_log_block_size) 

125 ) 

126 

127 if not self.valid_header(header): 

128 raise InvalidInputFormat("Invalid ExtFS header.") 

129 

130 return ValidChunk( 

131 start_offset=start_offset, 

132 end_offset=end_offset, 

133 )