rustubs/fs/
ustar.rs

1//! this asume a read-only; in memory filesystem, and we assume the FS has a
2//! static lifetime . This makes slice type much easier.
3use core::iter::Iterator;
4use core::str;
5
6// yes, I want this naming, shut up rust.
7#[allow(non_camel_case_types)]
8pub enum FileType {
9	NORMAL,
10	HLINK,
11	SYMLINK,
12	CHAR_DEV,
13	BLOCK_DEV,
14	DIR,
15	FIFO,
16	CONT,
17	EXTHDR_META,
18	EXTHDR_METANXT,
19	// RESERVED includes vender specifics ('A' - 'Z')
20	RESERVED(u8),
21}
22
23#[derive(Clone)]
24pub struct UstarArchiveIter<'a> {
25	pub archive: &'a [u8],
26	pub iter_curr: usize,
27}
28
29/// gives you an ustar file iterator over an read only u8 slice archive
30pub fn iter(archive: &[u8]) -> UstarArchiveIter<'_> {
31	UstarArchiveIter { archive, iter_curr: 0 }
32}
33
34impl<'a> Iterator for UstarArchiveIter<'a> {
35	type Item = UstarFile<'a>;
36	fn next(&mut self) -> Option<Self::Item> {
37		if self.iter_curr >= self.archive.len() {
38			return None;
39		}
40
41		let hdr = FileHdr::from_slice(&self.archive[self.iter_curr..]);
42		let hdr = hdr?;
43		if !hdr.is_ustar() {
44			return None;
45		}
46		let file_sz = hdr.size() as usize;
47		// file_start skips over the header (whose size is 512).
48		// file_end is non-inclusive
49		let file_start = self.iter_curr + 512;
50		let ret = Some(UstarFile {
51			hdr: hdr.clone(),
52			file: &self.archive[file_start..(file_start + file_sz)],
53		});
54		// increment: hdr size + file size, round up to next 512.
55		self.iter_curr += file_sz.next_multiple_of(512) + 512;
56		return ret;
57	}
58}
59
60impl FileType {
61	pub fn from_u8(flag: u8) -> Self {
62		match flag as char {
63			'0' | '\0' => Self::NORMAL,
64			'1' => Self::HLINK,
65			'2' => Self::SYMLINK,
66			'3' => Self::CHAR_DEV,
67			'5' => Self::DIR,
68			'6' => Self::FIFO,
69			'7' => Self::CONT,
70			'g' => Self::EXTHDR_META,
71			'x' => Self::EXTHDR_METANXT,
72			_ => Self::RESERVED(flag),
73		}
74	}
75}
76
77// Actually this is chanllenging: how do you justify lifetimes of these things?
78// - a "opened file"
79// - page cache that buffers the file content
80// - the reference to the backing file in the VMA...
81// well, it seems we need the abstraction of file descriptors, if we can't
82// guarantee the lifetime of a file to outlive ther tasks
83#[derive(Clone)]
84pub struct UstarFile<'a> {
85	pub hdr: FileHdr<'a>,
86	pub file: &'a [u8],
87}
88
89#[derive(Clone)]
90pub struct FileHdr<'a>(&'a [u8]);
91impl<'a> FileHdr<'a> {
92	pub fn name(&self) -> &str {
93		let file_name = to_cstr(&self.0[0..100]);
94		str::from_utf8(file_name.unwrap())
95			.unwrap()
96			.trim_matches(char::from(0))
97	}
98	pub fn owner(&self) -> &str {
99		let user_name = to_cstr(&self.0[265..265 + 32]);
100		str::from_utf8(user_name.unwrap()).unwrap()
101	}
102	pub fn owner_group(&self) -> &str {
103		let user_name = to_cstr(&self.0[297..297 + 32]);
104		str::from_utf8(user_name.unwrap()).unwrap()
105	}
106	pub fn size(&self) -> u32 {
107		let sz = &self.0[124..124 + 11];
108		oct2bin(sz)
109	}
110	pub fn is_ustar(&self) -> bool {
111		if let Ok(magic) = str::from_utf8(&self.0[257..(257 + 5)]) {
112			return magic == "ustar";
113		}
114		return false;
115	}
116	pub fn from_slice(ustar_slice: &'a [u8]) -> Option<Self> {
117		if ustar_slice.len() < 512 {
118			return None;
119		}
120		Some(Self(&ustar_slice[0..512]))
121	}
122}
123
124/// helper function to convert oct literals into number
125fn oct2bin(s: &[u8]) -> u32 {
126	let mut n: u32 = 0;
127	for u in s {
128		n *= 8;
129		let d = *u - b'0';
130		n += d as u32;
131	}
132	n
133}
134
135/// cut the slice at the first null
136fn to_cstr(s: &[u8]) -> Option<&[u8]> {
137	let mut end = 0;
138	for c in s {
139		if *c == 0 {
140			if end == 0 {
141				return None;
142			} else {
143				break;
144			}
145		}
146		end += 1;
147	}
148	return Some(&s[0..=end]);
149}