1use std::io::SeekFrom;
15use std::sync::Arc;
16
17use keramics_checksums::ReversedCrc32Context;
18use keramics_core::{DataStreamReference, ErrorTrace};
19use keramics_datetime::DateTime;
20use keramics_types::ByteString;
21
22use super::constants::*;
23use super::features::ExtFeatures;
24use super::file_entry::ExtFileEntry;
25use super::group_descriptor::ExtGroupDescriptor;
26use super::group_descriptor_table::ExtGroupDescriptorTable;
27use super::inode::ExtInode;
28use super::inode_table::ExtInodeTable;
29use super::path::ExtPath;
30use super::superblock::ExtSuperblock;
31
32pub struct ExtFileSystem {
34 data_stream: Option<DataStreamReference>,
36
37 features: ExtFeatures,
39
40 pub number_of_inodes: u32,
42
43 block_size: u32,
45
46 inode_size: u16,
48
49 inode_table: Arc<ExtInodeTable>,
51
52 metadata_checksum_seed: u32,
54
55 volume_label: ByteString,
57
58 pub last_mount_path: ByteString,
60
61 pub last_mount_time: DateTime,
63
64 pub last_written_time: DateTime,
66}
67
68impl ExtFileSystem {
69 pub fn new() -> Self {
71 Self {
72 data_stream: None,
73 volume_label: ByteString::new(),
74 features: ExtFeatures::new(),
75 number_of_inodes: 0,
76 block_size: 0,
77 inode_size: 0,
78 inode_table: Arc::new(ExtInodeTable::new()),
79 metadata_checksum_seed: 0,
80 last_mount_path: ByteString::new(),
81 last_mount_time: DateTime::NotSet,
82 last_written_time: DateTime::NotSet,
83 }
84 }
85
86 pub fn get_format_version(&self) -> u8 {
88 self.features.get_format_version()
89 }
90
91 pub fn get_compatible_feature_flags(&self) -> u32 {
93 self.features.compatible_feature_flags
94 }
95
96 pub fn get_incompatible_feature_flags(&self) -> u32 {
98 self.features.incompatible_feature_flags
99 }
100
101 pub fn get_read_only_compatible_feature_flags(&self) -> u32 {
103 self.features.read_only_compatible_feature_flags
104 }
105
106 pub fn get_volume_label(&self) -> &ByteString {
108 &self.volume_label
109 }
110
111 pub fn get_file_entry_by_identifier(
113 &self,
114 inode_number: u32,
115 ) -> Result<ExtFileEntry, ErrorTrace> {
116 let data_stream: &DataStreamReference = match self.data_stream.as_ref() {
117 Some(data_stream) => data_stream,
118 None => {
119 return Err(keramics_core::error_trace_new!("Missing data stream"));
120 }
121 };
122 if self.features.is_unsupported() {
123 return Err(keramics_core::error_trace_new!(
124 "Ext file system has unsupported features"
125 ));
126 }
127 if inode_number == 0 || inode_number > self.number_of_inodes {
128 return Err(keramics_core::error_trace_new!(format!(
129 "Invalid inode number: {} value out of bounds",
130 inode_number
131 )));
132 }
133 let inode: ExtInode = match self.inode_table.get_inode(data_stream, inode_number) {
134 Ok(inode) => inode,
135 Err(mut error) => {
136 keramics_core::error_trace_add_frame!(
137 error,
138 format!("Unable to retrieve inode: {}", inode_number)
139 );
140 return Err(error);
141 }
142 };
143 Ok(ExtFileEntry::new(
144 data_stream,
145 &self.inode_table,
146 inode_number,
147 inode,
148 None,
149 ))
150 }
151
152 pub fn get_file_entry_by_path(
154 &self,
155 path: &ExtPath,
156 ) -> Result<Option<ExtFileEntry>, ErrorTrace> {
157 if path.is_empty() || path.components[0].len() != 0 {
158 return Ok(None);
159 }
160 let result: Option<ExtFileEntry> = match self.get_root_directory() {
161 Ok(result) => result,
162 Err(mut error) => {
163 keramics_core::error_trace_add_frame!(error, "Unable to retrieve root directory");
164 return Err(error);
165 }
166 };
167 let mut file_entry: ExtFileEntry = match result {
168 Some(file_entry) => file_entry,
169 None => return Ok(None),
170 };
171 for path_component in path.components[1..].iter() {
173 let result: Option<ExtFileEntry> =
174 match file_entry.get_sub_file_entry_by_name(path_component) {
175 Ok(result) => result,
176 Err(mut error) => {
177 keramics_core::error_trace_add_frame!(
178 error,
179 format!(
180 "Unable to retrieve sub file entry: {}",
181 path_component.to_string()
182 )
183 );
184 return Err(error);
185 }
186 };
187 file_entry = match result {
188 Some(file_entry) => file_entry,
189 None => return Ok(None),
190 };
191 }
192 Ok(Some(file_entry))
193 }
194
195 pub fn get_root_directory(&self) -> Result<Option<ExtFileEntry>, ErrorTrace> {
197 if self.number_of_inodes == 0 {
198 return Ok(None);
199 }
200 match self.get_file_entry_by_identifier(EXT_ROOT_DIRECTORY_IDENTIFIER) {
201 Ok(file_entry) => Ok(Some(file_entry)),
202 Err(mut error) => {
203 keramics_core::error_trace_add_frame!(
204 error,
205 format!(
206 "Unable to retrieve file entry: {}",
207 EXT_ROOT_DIRECTORY_IDENTIFIER
208 )
209 );
210 Err(error)
211 }
212 }
213 }
214
215 pub fn read_data_stream(
217 &mut self,
218 data_stream: &DataStreamReference,
219 ) -> Result<(), ErrorTrace> {
220 match self.read_block_groups(data_stream) {
221 Ok(_) => {}
222 Err(mut error) => {
223 keramics_core::error_trace_add_frame!(error, "Unable to read block groups");
224 return Err(error);
225 }
226 }
227 self.data_stream = Some(data_stream.clone());
228
229 Ok(())
230 }
231
232 fn read_block_groups(&mut self, data_stream: &DataStreamReference) -> Result<(), ErrorTrace> {
234 let mut block_group_number: u32 = 0;
235 let mut block_group_offset: u64 = 0;
236 let mut block_group_size: u64 = 0;
237 let mut exponent3: u32 = 3;
238 let mut exponent5: u32 = 5;
239 let mut exponent7: u32 = 7;
240 let mut meta_group_number: u32 = 0;
241 let mut meta_group_start_block_number: u32 = 0;
242 let mut number_of_block_groups: u64 = 0;
243 let mut number_of_block_groups_per_meta_group: u32 = 0;
244 let mut number_of_inodes_per_block_group: u32 = 0;
245 let mut group_descriptors: Vec<ExtGroupDescriptor> = Vec::new();
246
247 loop {
248 if exponent3 < block_group_number {
249 exponent3 *= 3;
250 }
251 if exponent5 < block_group_number {
252 exponent5 *= 5;
253 }
254 if exponent7 < block_group_number {
255 exponent7 *= 7;
256 }
257 let block_group_has_superblock: bool = if block_group_number == 0 {
259 true
260 } else if !self.features.has_sparse_superblock() {
262 true
263 } else if self.features.has_sparse_superblock2() {
265 false
266 } else if block_group_number == 1
268 || block_group_number == exponent3
269 || block_group_number == exponent5
270 || block_group_number == exponent7
271 {
272 true
273 } else {
274 false
275 };
276 if block_group_has_superblock {
277 let mut superblock_offset: u64 = block_group_offset;
278
279 if block_group_offset == 0 || self.block_size == 1024 {
280 superblock_offset += 1024;
281 }
282 if block_group_number == 0 {
283 let mut superblock: ExtSuperblock = ExtSuperblock::new();
284
285 match superblock
286 .read_at_position(data_stream, SeekFrom::Start(superblock_offset))
287 {
288 Ok(_) => {}
289 Err(mut error) => {
290 keramics_core::error_trace_add_frame!(
291 error,
292 format!(
293 "Unable to read superblock at offset: {} (0x{:08x})",
294 superblock_offset, superblock_offset
295 )
296 );
297 return Err(error);
298 }
299 }
300 self.features.initialize(&superblock);
301
302 number_of_block_groups = superblock.get_number_of_block_groups();
303 block_group_size = superblock.get_block_group_size();
304
305 self.number_of_inodes = superblock.number_of_inodes;
306 self.block_size = superblock.block_size;
307 self.inode_size = superblock.inode_size;
308
309 self.volume_label = superblock.volume_label;
310
311 self.last_mount_path = superblock.last_mount_path;
313
314 if superblock.last_mount_time.timestamp != 0 {
315 self.last_mount_time = DateTime::PosixTime32(superblock.last_mount_time);
316 }
317 if superblock.last_written_time.timestamp != 0 {
318 self.last_written_time =
319 DateTime::PosixTime32(superblock.last_written_time);
320 }
321 number_of_inodes_per_block_group = superblock.number_of_inodes_per_block_group;
322
323 if self.features.has_meta_block_groups() {
324 let group_descriptor_size: u32 = self.features.get_group_descriptor_size();
325 number_of_block_groups_per_meta_group =
326 superblock.block_size / group_descriptor_size;
327 meta_group_start_block_number = superblock.first_meta_block_group
328 * number_of_block_groups_per_meta_group;
329 }
330 self.metadata_checksum_seed = match superblock.metadata_checksum_seed {
331 Some(metadata_checksum_seed) => metadata_checksum_seed,
332 None => {
333 let mut crc32_context: ReversedCrc32Context =
334 ReversedCrc32Context::new(0x82f63b78, 0);
335 crc32_context.update(&superblock.file_system_identifier);
336 crc32_context.finalize()
337 }
338 };
339 } else {
340 let mut superblock: ExtSuperblock = ExtSuperblock::new();
341
342 match superblock
343 .read_at_position(data_stream, SeekFrom::Start(superblock_offset))
344 {
345 Ok(_) => {
346 }
348 Err(_) => {}
350 }
351 }
352 }
353 let mut block_group_has_group_descriptors: bool = false;
358 let mut meta_group_block_group_number: u32 = 0;
359
360 if !self.features.has_meta_block_groups()
361 || block_group_number < meta_group_start_block_number
362 {
363 block_group_has_group_descriptors = block_group_has_superblock;
364 } else {
365 meta_group_block_group_number =
366 block_group_number % number_of_block_groups_per_meta_group;
367
368 if meta_group_block_group_number == 0
369 || meta_group_block_group_number == 1
370 || meta_group_block_group_number == number_of_block_groups_per_meta_group - 1
371 {
372 block_group_has_group_descriptors = true;
373 }
374 }
375 if block_group_has_group_descriptors {
376 let mut group_descriptor_offset: u64 = block_group_offset;
377
378 if self.block_size == 1024 {
379 group_descriptor_offset += 1024;
380 }
381 if block_group_has_superblock {
382 group_descriptor_offset += self.block_size as u64;
383 }
384 let first_group_number: u32 = if !self.features.has_meta_block_groups()
385 || block_group_number < meta_group_start_block_number
386 {
387 0
388 } else {
389 meta_group_start_block_number
390 + (meta_group_number * number_of_block_groups_per_meta_group)
391 };
392 let number_of_group_descriptors: u32 = if !self.features.has_meta_block_groups() {
393 number_of_block_groups as u32
394 } else if block_group_number < meta_group_start_block_number {
395 meta_group_start_block_number
396 } else {
397 number_of_block_groups_per_meta_group
398 };
399 let mut group_descriptor_table: ExtGroupDescriptorTable =
400 ExtGroupDescriptorTable::new();
401 group_descriptor_table.initialize(
402 &self.features,
403 first_group_number,
404 number_of_group_descriptors,
405 );
406 match group_descriptor_table
407 .read_at_position(data_stream, SeekFrom::Start(group_descriptor_offset))
408 {
409 Ok(_) => {}
410 Err(mut error) => {
411 keramics_core::error_trace_add_frame!(
412 error,
413 format!(
414 "Unable to read group descriptor table at offset: {} (0x{:08x})",
415 group_descriptor_offset, group_descriptor_offset
416 )
417 );
418 return Err(error);
419 }
420 }
421 if !self.features.has_meta_block_groups()
422 || block_group_number < meta_group_start_block_number
423 {
424 if block_group_number == 0 {
425 for group_descriptor in group_descriptor_table.entries.drain(0..) {
426 group_descriptors.push(group_descriptor);
427 }
428 }
429 } else if meta_group_block_group_number == 0 {
430 for group_descriptor in group_descriptor_table.entries.drain(0..) {
431 group_descriptors.push(group_descriptor);
432 }
433 } else if meta_group_block_group_number == number_of_block_groups_per_meta_group - 1
434 {
435 meta_group_number += 1;
436 };
437 }
438 block_group_number += 1;
442 block_group_offset += block_group_size;
443
444 if block_group_number as u64 >= number_of_block_groups {
445 break;
446 }
447 }
448 if number_of_inodes_per_block_group > 0 {
449 match Arc::get_mut(&mut self.inode_table) {
450 Some(inode_table) => match inode_table.initialize(
451 &self.features,
452 self.block_size,
453 self.inode_size,
454 number_of_inodes_per_block_group,
455 &mut group_descriptors,
456 ) {
457 Ok(_) => {}
458 Err(mut error) => {
459 keramics_core::error_trace_add_frame!(
460 error,
461 "Unable to initialize inode table"
462 );
463 return Err(error);
464 }
465 },
466 None => {
467 return Err(keramics_core::error_trace_new!(
468 "Unable to obtain mutable reference to inode table"
469 ));
470 }
471 };
472 }
473 Ok(())
476 }
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482
483 use std::path::PathBuf;
484
485 use keramics_core::open_os_data_stream;
486
487 fn get_file_system() -> Result<ExtFileSystem, ErrorTrace> {
488 let mut file_system: ExtFileSystem = ExtFileSystem::new();
489
490 let path_buf: PathBuf = PathBuf::from("../test_data/ext/ext2.raw");
491 let data_stream: DataStreamReference = open_os_data_stream(&path_buf)?;
492 file_system.read_data_stream(&data_stream)?;
493
494 Ok(file_system)
495 }
496
497 #[test]
498 fn test_get_format_version() -> Result<(), ErrorTrace> {
499 let file_system: ExtFileSystem = get_file_system()?;
500
501 let format_version: u8 = file_system.get_format_version();
502 assert_eq!(format_version, 2);
503
504 Ok(())
505 }
506
507 #[test]
508 fn test_get_feature_flags() -> Result<(), ErrorTrace> {
509 let file_system: ExtFileSystem = get_file_system()?;
510
511 let feature_flags: u32 = file_system.get_compatible_feature_flags();
512 assert_eq!(feature_flags, 0x00000038);
513
514 let feature_flags: u32 = file_system.get_incompatible_feature_flags();
515 assert_eq!(feature_flags, 0x00000002);
516
517 let feature_flags: u32 = file_system.get_read_only_compatible_feature_flags();
518 assert_eq!(feature_flags, 0x00000003);
519
520 Ok(())
521 }
522
523 #[test]
526 fn test_get_file_entry_by_identifier() -> Result<(), ErrorTrace> {
527 let file_system: ExtFileSystem = get_file_system()?;
528
529 let file_entry: ExtFileEntry = file_system.get_file_entry_by_identifier(12)?;
530
531 assert_eq!(file_entry.inode_number, 12);
532
533 let name: Option<&ByteString> = file_entry.get_name();
534 assert!(name.is_none());
535
536 Ok(())
537 }
538
539 #[test]
540 fn test_get_file_entry_by_path() -> Result<(), ErrorTrace> {
541 let file_system: ExtFileSystem = get_file_system()?;
542
543 let ext_path: ExtPath = ExtPath::from("/emptyfile");
544 let file_entry: ExtFileEntry = file_system.get_file_entry_by_path(&ext_path)?.unwrap();
545
546 assert_eq!(file_entry.inode_number, 12);
547
548 let ext_path: ExtPath = ExtPath::from("/testdir1/testfile1");
549 let file_entry: ExtFileEntry = file_system.get_file_entry_by_path(&ext_path)?.unwrap();
550
551 assert_eq!(file_entry.inode_number, 14);
552
553 let name: &ByteString = file_entry.get_name().unwrap();
554 assert_eq!(name.to_string(), "testfile1");
555
556 Ok(())
557 }
558
559 #[test]
560 fn test_get_root_directory() -> Result<(), ErrorTrace> {
561 let file_system: ExtFileSystem = get_file_system()?;
562
563 let file_entry: ExtFileEntry = file_system.get_root_directory()?.unwrap();
564
565 assert_eq!(file_entry.inode_number, 2);
566
567 Ok(())
568 }
569
570 #[test]
571 fn test_read_data_stream() -> Result<(), ErrorTrace> {
572 let mut file_system: ExtFileSystem = ExtFileSystem::new();
573
574 let path_buf: PathBuf = PathBuf::from("../test_data/ext/ext2.raw");
575 let data_stream: DataStreamReference = open_os_data_stream(&path_buf)?;
576 file_system.read_data_stream(&data_stream)?;
577
578 assert_eq!(file_system.block_size, 1024);
579 assert_eq!(file_system.inode_size, 128);
580
581 Ok(())
582 }
583
584 #[test]
585 fn test_read_block_groups() -> Result<(), ErrorTrace> {
586 let mut file_system: ExtFileSystem = ExtFileSystem::new();
587
588 let path_buf: PathBuf = PathBuf::from("../test_data/ext/ext2.raw");
589 let data_stream: DataStreamReference = open_os_data_stream(&path_buf)?;
590 file_system.read_block_groups(&data_stream)?;
591
592 assert_eq!(file_system.block_size, 1024);
593 assert_eq!(file_system.inode_size, 128);
594
595 Ok(())
596 }
597}