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		self.iter_curr += (((file_sz + 511) / 512) + 1) * 512;
55		return ret;
56	}
57}
58
59impl FileType {
60	pub fn from_u8(flag: u8) -> Self {
61		match flag as char {
62			'0' | '\0' => Self::NORMAL,
63			'1' => Self::HLINK,
64			'2' => Self::SYMLINK,
65			'3' => Self::CHAR_DEV,
66			'5' => Self::DIR,
67			'6' => Self::FIFO,
68			'7' => Self::CONT,
69			'g' => Self::EXTHDR_META,
70			'x' => Self::EXTHDR_METANXT,
71			_ => Self::RESERVED(flag),
72		}
73	}
74}
75
76// Actually this is chanllenging: how do you justify lifetimes of these things?
77// - a "opened file"
78// - page cache that buffers the file content
79// - the reference to the backing file in the VMA...
80// well, it seems we need the abstraction of file descriptors, if we can't
81// guarantee the lifetime of a file to outlive ther tasks
82#[derive(Clone)]
83pub struct UstarFile<'a> {
84	pub hdr: FileHdr<'a>,
85	pub file: &'a [u8],
86}
87
88#[derive(Clone)]
89pub struct FileHdr<'a>(&'a [u8]);
90impl<'a> FileHdr<'a> {
91	pub fn name(&self) -> &str {
92		let file_name = to_cstr(&self.0[0..100]);
93		str::from_utf8(file_name.unwrap())
94			.unwrap()
95			.trim_matches(char::from(0))
96	}
97	pub fn owner(&self) -> &str {
98		let user_name = to_cstr(&self.0[265..265 + 32]);
99		str::from_utf8(user_name.unwrap()).unwrap()
100	}
101	pub fn owner_group(&self) -> &str {
102		let user_name = to_cstr(&self.0[297..297 + 32]);
103		str::from_utf8(user_name.unwrap()).unwrap()
104	}
105	pub fn size(&self) -> u32 {
106		let sz = &self.0[124..124 + 11];
107		oct2bin(sz)
108	}
109	pub fn is_ustar(&self) -> bool {
110		if let Ok(magic) = str::from_utf8(&self.0[257..(257 + 5)]) {
111			return magic == "ustar";
112		}
113		return false;
114	}
115	pub fn from_slice(ustar_slice: &'a [u8]) -> Option<Self> {
116		if ustar_slice.len() < 512 {
117			return None;
118		}
119		Some(Self(&ustar_slice[0..512]))
120	}
121}
122
123/// helper function to convert oct literals into number
124fn oct2bin(s: &[u8]) -> u32 {
125	let mut n: u32 = 0;
126	for u in s {
127		n *= 8;
128		let d = *u - b'0';
129		n += d as u32;
130	}
131	n
132}
133
134/// cut the slice at the first null
135fn to_cstr(s: &[u8]) -> Option<&[u8]> {
136	let mut end = 0;
137	for c in s {
138		if *c == 0 {
139			if end == 0 {
140				return None;
141			} else {
142				break;
143			}
144		}
145		end += 1;
146	}
147	return Some(&s[0..=end]);
148}