tar_loader: fixups - from_bytes does not need a mutable reference - add has_magic method to check the magic field - fix name to return a string w/o embedded nul's - rewrite parse_octal to use iterators - rewrite find_file to use methods, to stop searching at the first invalid (according to has_magic) header, and to correctly check for end-of-flash - change copy_file to check the offset instead of the file size when deciding if a file was found--zero-length files can and do exist Bug: 294433731 Change-Id: Ic769517bfbcc51e2e8c4f1cb485d3bb6338179b4
diff --git a/utils/src/tar_loader.rs b/utils/src/tar_loader.rs index 10a3024..94ff468 100644 --- a/utils/src/tar_loader.rs +++ b/utils/src/tar_loader.rs
@@ -1,6 +1,6 @@ // Trivial tar loader. -use core::mem::transmute; +use core::mem::{size_of, transmute}; use core::ptr; use matcha_hal::dprintf; @@ -54,12 +54,21 @@ } impl TarHeader { - pub fn from_bytes(b: &mut [u8]) -> TarHeader { - unsafe { ptr::read(b.as_ptr() as *const TarHeader) } + pub fn from_bytes(b: &[u8]) -> TarHeader { + unsafe { b.as_ptr().cast::<TarHeader>().read() } + } + + pub fn has_magic(&self) -> bool { + self.magic[..5] == *b"ustar" } pub fn name(&self) -> &str { - core::str::from_utf8(&self.name).unwrap() + if let Some(len) = self.name.iter().position(|&x| x == 0) { + core::str::from_utf8(&self.name[..len]) + } else { + core::str::from_utf8(&self.name) + } + .unwrap_or("<invalid>") } pub fn size(&self) -> u32 { @@ -68,28 +77,33 @@ } fn parse_octal(text: &[u8]) -> u32 { - let mut n: u32 = 0; - for x in text.iter() { - if *x == (0 as u8) { - break; - } - n = (n << 3) | ((*x as u32) - ('0' as u32)); - } - return n; + text.iter() + .take_while(|&&x| x != 0) + .fold(0u32, |acc, &x| (acc << 3) + (x as u32 - '0' as u32)) } pub unsafe fn find_file(name: &str) -> (u32, u32) { let mut cursor = EFLASH_START; - while cursor < EFLASH_END { - let tar_header: *const TarHeader = transmute(cursor); - cursor += 512; - let tar_name = core::str::from_utf8(&(*tar_header).name).unwrap(); - if tar_name.contains(name) { - let tar_size = parse_octal(&(*tar_header).size); + while (cursor + 512) < EFLASH_END { + let tar_header = &*(cursor as *const TarHeader); + if !tar_header.has_magic() { + // Our tar files have the "ustar" magic field; use it + // to avoid scanning all EFLASH. + break; + } + cursor += size_of::<TarHeader>() as u32; + let tar_name = tar_header.name(); + let tar_size = tar_header.size(); + if tar_name == name { return (cursor, tar_size); } else { - let tar_size = parse_octal(&(*tar_header).size); - cursor += (tar_size + 511) & !511; + fn roundup(a: u32, b: u32) -> u32 { + ((a + b - 1) / b) * b + } + cursor += roundup( + tar_size, + size_of::<TarHeader>() as u32, + ); } } @@ -100,9 +114,14 @@ // TODO(sleffler): rethink api, e.g. supply a range for bounds check pub unsafe fn copy_file(name: &str, offset: u32) -> bool { let (cursor, size) = find_file(name); - if size == 0 { - return false; + if cursor != 0 { + ptr::copy_nonoverlapping::<u8>( + transmute(cursor), + transmute(offset), + size as usize, + ); + true + } else { + false } - ptr::copy_nonoverlapping::<u8>(transmute(cursor), transmute(offset), size as usize); - true }