Skip to main content

corevm_host/fs/
operations.rs

1use super::*;
2
3/// File system node.
4pub enum Node {
5	File(File),
6	Dir(Dir),
7}
8
9impl Node {
10	pub fn open<R: ReadBlock>(block_ref: &BlockRef, block_reader: &mut R) -> Result<Self, Error> {
11		let mut reader = NodeReader::new(block_ref, block_reader)?;
12		let node = match reader.file().main_block().kind() {
13			NodeKind::File => {
14				let file = reader.into_file();
15				Self::File(file)
16			},
17			NodeKind::Dir => {
18				let dir = Dir::decode(&mut reader).map_err(|_| Error::Io)?;
19				Self::Dir(dir)
20			},
21		};
22		Ok(node)
23	}
24}
25
26// TODO @ivan Resolving a path involves reading and decoding a directory in full, although we only
27// need one entry. Ideally we need to be able to check that the entry exists without reading and
28// decoding all entries.
29
30/// A directory stored in the block storage.
31///
32/// Stored as file with a different [`NodeKind`] in the main block.
33#[derive(Encode, Decode, Debug)]
34pub struct Dir(pub VecMap<FileName, BlockRef>);
35
36impl Dir {
37	pub fn open<R: ReadBlock>(block_ref: &BlockRef, block_reader: &mut R) -> Result<Self, Error> {
38		let mut reader = NodeReader::new(block_ref, block_reader)?;
39		if reader.file().main_block().kind() != NodeKind::Dir {
40			return Err(Error::Node);
41		}
42		let dir = Self::decode(&mut reader).map_err(|_| Error::Io)?;
43		Ok(dir)
44	}
45}
46
47/// A file stored in the block storage.
48///
49/// Might actually refer to a directory. Check [`MainBlock::kind`] to determine that.
50pub struct File {
51	main_block: MainBlock,
52	current_block: Option<(usize, FileBlock)>,
53	position: u64,
54}
55
56impl File {
57	pub fn main_block(&self) -> &MainBlock {
58		&self.main_block
59	}
60
61	pub fn position(&self) -> u64 {
62		self.position
63	}
64
65	pub fn seek(&mut self, position: u64) -> Result<(), Error> {
66		if position > self.main_block.file_size {
67			return Err(Error::Io);
68		}
69		self.position = position;
70		Ok(())
71	}
72
73	pub fn open<R: ReadBlock>(main_block_ref: &BlockRef, reader: &mut R) -> Result<Self, Error> {
74		let block = reader.read_block(main_block_ref)?;
75		let main_block = MainBlock::decode(block)?;
76		Ok(Self { main_block, current_block: None, position: 0 })
77	}
78
79	pub fn from_main_block(main_block: MainBlock) -> Self {
80		Self { main_block, current_block: None, position: 0 }
81	}
82
83	pub fn read<R: ReadBlock>(&mut self, buf: &mut [u8], reader: &mut R) -> Result<usize, Error> {
84		let n = (buf.len() as u64).min(self.main_block.file_size - self.position);
85		if n == 0 {
86			return Ok(0);
87		}
88		let next_position = self.position + n;
89		let n = n as usize;
90		let first_block_size = self.main_block.first_block.len() as u64;
91		let mut i = self.get_block_index(self.position);
92		let mut buf_position = 0;
93		if i == usize::MAX {
94			// Copy from the first block.
95			let a = self.position as usize;
96			let b = next_position.min(first_block_size) as usize;
97			let m = b - a;
98			buf[..m].copy_from_slice(&self.main_block.first_block.0[a..b]);
99			self.position += m as u64;
100			buf_position += m;
101			i = 0;
102		}
103		// Copy from the rest of the blocks.
104		while self.position != next_position {
105			let block = match &mut self.current_block {
106				Some((block_index, block)) if *block_index == i => block,
107				block => {
108					let data = reader.read_block(&self.main_block.block_refs[i])?;
109					let new_block = FileBlock::new(data)?;
110					&mut block.insert((i, new_block)).1
111				},
112			};
113			let a = (self.position - first_block_size - i as u64 * MAX_BLOCK_SIZE as u64) as usize;
114			let m = ((next_position - self.position) as usize).min(block.len() - a);
115			buf[buf_position..buf_position + m].copy_from_slice(&block.0[a..a + m]);
116			self.position += m as u64;
117			buf_position += m;
118			i += 1;
119		}
120		Ok(n)
121	}
122
123	pub fn read_exact<R: ReadBlock>(
124		&mut self,
125		buf: &mut [u8],
126		reader: &mut R,
127	) -> Result<(), Error> {
128		let n = self.read(buf, reader)?;
129		if n != buf.len() {
130			return Err(Error::Io);
131		}
132		Ok(())
133	}
134
135	pub fn read_to_end<R: ReadBlock>(
136		&mut self,
137		buf: &mut Vec<u8>,
138		reader: &mut R,
139	) -> Result<usize, Error> {
140		let remaining = (self.main_block.file_size - self.position) as usize;
141		let old_len = buf.len();
142		buf.resize(old_len + remaining, 0_u8);
143		self.read_exact(&mut buf[old_len..], reader)?;
144		Ok(remaining)
145	}
146
147	fn get_block_index(&self, mut position: u64) -> usize {
148		debug_assert!(position < self.main_block.file_size);
149		let first_block_size = self.main_block.first_block.len() as u64;
150		if position < first_block_size {
151			return usize::MAX;
152		}
153		position -= first_block_size;
154		(position / self.main_block.block_size) as usize
155	}
156}
157
158/// Reads a node (file or directory) from the block storage.
159pub struct NodeReader<R: ReadBlock> {
160	reader: R,
161	file: File,
162}
163
164impl<R: ReadBlock> NodeReader<R> {
165	pub fn new(main_block_ref: &BlockRef, mut reader: R) -> Result<Self, Error> {
166		let file = File::open(main_block_ref, &mut reader)?;
167		Ok(Self { reader, file })
168	}
169
170	pub fn from_main_block(main_block: MainBlock, reader: R) -> Self {
171		let file = File::from_main_block(main_block);
172		Self { reader, file }
173	}
174
175	pub fn file(&self) -> &File {
176		&self.file
177	}
178
179	pub fn into_file(self) -> File {
180		self.file
181	}
182
183	pub fn into_inner(self) -> R {
184		self.reader
185	}
186
187	pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
188		self.file.read(buf, &mut self.reader)
189	}
190
191	pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> {
192		self.file.read_exact(buf, &mut self.reader)
193	}
194
195	pub fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error> {
196		self.file.read_to_end(buf, &mut self.reader)
197	}
198
199	pub fn seek(&mut self, position: u64) -> Result<(), Error> {
200		self.file.seek(position)
201	}
202}
203
204impl<R: ReadBlock> codec::Input for NodeReader<R> {
205	fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {
206		let remaining = self.file.main_block().file_size() - self.file.position();
207		Ok(remaining.try_into().ok())
208	}
209
210	fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> {
211		self.read_exact(into).map_err(|_| "I/o error")?;
212		Ok(())
213	}
214}
215
216/// Write a node (file or directory) to the block storage.
217pub struct NodeWriter<W: WriteBlock> {
218	writer: W,
219	buf: Vec<u8>,
220	kind: NodeKind,
221	file_size: u64,
222	block_size: usize,
223	first_block_size: usize,
224	position: u64,
225	block_refs: Vec<BlockRef>,
226	first_block: Vec<u8>,
227	service_id: ServiceId,
228}
229
230impl<W: WriteBlock> NodeWriter<W> {
231	pub fn new(
232		service_id: ServiceId,
233		writer: W,
234		file_size: u64,
235		block_size: usize,
236	) -> Result<Self, Error> {
237		Self::do_new(service_id, writer, NodeKind::File, file_size, block_size)
238	}
239
240	pub fn new_dir(
241		service_id: ServiceId,
242		writer: W,
243		file_size: u64,
244		block_size: usize,
245	) -> Result<Self, Error> {
246		Self::do_new(service_id, writer, NodeKind::Dir, file_size, block_size)
247	}
248
249	fn do_new(
250		service_id: ServiceId,
251		writer: W,
252		kind: NodeKind,
253		file_size: u64,
254		block_size: usize,
255	) -> Result<Self, Error> {
256		let first_block_size = {
257			let mut num_blocks = file_size.div_ceil(block_size as u64) as usize;
258			let mut first_block_size = MAX_BLOCK_SIZE as u64;
259			// Two steps of fixed-point iteration.
260			for _ in 0..2 {
261				let metadata_len =
262					main_block_metadata_encoded_len(file_size, block_size as u64, num_blocks)
263						.ok_or(Error::Block)? as u64;
264				first_block_size = (MAX_BLOCK_SIZE as u64)
265					.checked_sub(metadata_len)
266					.ok_or(Error::Block)?
267					.min(file_size);
268				let new_num_blocks =
269					(file_size - first_block_size).div_ceil(block_size as u64) as usize;
270				if num_blocks == new_num_blocks {
271					break;
272				}
273				num_blocks = new_num_blocks;
274			}
275			first_block_size as usize
276		};
277		let buf_capacity = (block_size as u64).min(file_size) as usize;
278		Ok(Self {
279			writer,
280			kind,
281			file_size,
282			block_size,
283			first_block_size,
284			position: 0,
285			buf: Vec::with_capacity(buf_capacity.max(first_block_size)),
286			block_refs: Vec::new(),
287			first_block: Vec::new(),
288			service_id,
289		})
290	}
291
292	pub fn write_all(&mut self, data: &[u8]) -> Result<(), Error> {
293		let next_position = self.position + data.len() as u64;
294		if next_position > self.file_size {
295			return Err(Error::Io);
296		}
297		let mut slice = data;
298		while !slice.is_empty() {
299			let max_block_size =
300				if self.first_block.is_empty() { self.first_block_size } else { self.block_size };
301			let n = slice.len().min(max_block_size - self.buf.len());
302			let (chunk, rest) = slice.split_at(n);
303			self.buf.extend_from_slice(chunk);
304			if self.buf.len() == max_block_size {
305				self.write_block()?;
306			}
307			slice = rest;
308		}
309		self.position = next_position;
310		Ok(())
311	}
312
313	pub fn read_from<R: HostFileRead>(&mut self, reader: &mut R) -> Result<(), Error> {
314		let mut remaining = reader.remaining_len()?;
315		let next_position = self.position.checked_add(remaining).ok_or(Error::Io)?;
316		if next_position > self.file_size {
317			return Err(Error::Io);
318		}
319		while remaining != 0 {
320			let max_block_size =
321				if self.first_block.is_empty() { self.first_block_size } else { MAX_BLOCK_SIZE };
322			let old_len = self.buf.len();
323			let n = remaining.min(max_block_size as u64 - old_len as u64) as usize;
324			self.buf.resize(old_len + n, 0_u8);
325			reader.read_exact(&mut self.buf[old_len..])?;
326			if self.buf.len() == max_block_size {
327				self.write_block()?;
328			}
329			remaining -= n as u64;
330		}
331		self.position = next_position;
332		Ok(())
333	}
334
335	/// Finish writing the node.
336	///
337	/// Returns main block hash and the underlying block writer.
338	pub fn finish(mut self) -> Result<(BlockRef, W), Error> {
339		if !self.buf.is_empty() {
340			self.write_block()?;
341		}
342		if self.position != self.file_size {
343			return Err(Error::Io);
344		}
345		// Write main block.
346		let main_block = MainBlock {
347			kind: self.kind,
348			file_size: self.file_size,
349			block_size: self.block_size as u64,
350			block_refs: core::mem::take(&mut self.block_refs),
351			first_block: FileBlock(core::mem::take(&mut self.first_block).into()),
352		};
353		debug_assert!(validate_main_block(
354			main_block.file_size,
355			main_block.block_size,
356			&main_block.block_refs,
357			&main_block.first_block
358		)
359		.is_ok());
360		self.buf.clear();
361		main_block.encode_to(&mut self.buf);
362		self.writer.write_block(self.service_id, &self.buf[..])?;
363		let main_block_ref =
364			BlockRef { service_id: self.service_id, hash: Hash::digest(&self.buf[..]) };
365		Ok((main_block_ref, self.writer))
366	}
367
368	fn write_block(&mut self) -> Result<(), Error> {
369		if self.first_block.is_empty() {
370			self.first_block = core::mem::take(&mut self.buf);
371			self.buf = Vec::with_capacity((self.block_size as u64).min(self.file_size) as usize);
372		} else {
373			self.block_refs
374				.push(BlockRef { service_id: self.service_id, hash: Hash::digest(&self.buf[..]) });
375			self.writer.write_block(self.service_id, &self.buf[..])?;
376			self.buf.clear();
377		}
378		Ok(())
379	}
380}
381
382// This is a workaround for `codec::Output::write` being infallible.
383struct FallibleOutput<'a, W: WriteBlock> {
384	writer: &'a mut NodeWriter<W>,
385	error: Option<Error>,
386}
387
388impl<W: WriteBlock> codec::Output for FallibleOutput<'_, W> {
389	fn write(&mut self, bytes: &[u8]) {
390		if self.error.is_some() {
391			return;
392		}
393		if let Err(e) = self.writer.write_all(bytes) {
394			self.error = Some(e);
395		}
396	}
397}
398
399/// Copy the file from the host file system to the block storage.
400pub fn copy_file_in<R: HostFileRead, W: WriteBlock>(
401	host_file_reader: &mut R,
402	service_id: ServiceId,
403	block_writer: &mut W,
404	block_size: usize,
405) -> Result<BlockRef, Error> {
406	let file_size = host_file_reader.remaining_len()?;
407	let mut writer = NodeWriter::new(service_id, block_writer, file_size, block_size)?;
408	writer.read_from(host_file_reader)?;
409	let (main_block_ref, _writer) = writer.finish()?;
410	Ok(main_block_ref)
411}
412
413/// Copy the file referenced by `main_block_ref` from the block storage to the host file system.
414pub fn copy_file_out<R: ReadBlock, W: HostFileWrite + ?Sized>(
415	main_block_ref: &BlockRef,
416	block_reader: &mut R,
417	host_file_writer: &mut W,
418) -> Result<(), Error> {
419	let mut reader = NodeReader::new(main_block_ref, block_reader)?;
420	if reader.file.main_block.kind != NodeKind::File {
421		// Not a file.
422		return Err(Error::Node);
423	}
424	do_copy_file_out(&mut reader, host_file_writer)?;
425	Ok(())
426}
427
428fn do_copy_file_out<R: ReadBlock, W: HostFileWrite + ?Sized>(
429	reader: &mut NodeReader<R>,
430	writer: &mut W,
431) -> Result<(), Error> {
432	let mut buf = vec![0_u8; MAX_BLOCK_SIZE];
433	loop {
434		let n = reader.read(&mut buf[..])?;
435		if n == 0 {
436			break;
437		}
438		writer.write_all(&buf[..n])?;
439	}
440	Ok(())
441}
442
443/// Create directory in the block storage.
444pub fn create_dir<W: WriteBlock>(
445	dir: &Dir,
446	service_id: ServiceId,
447	block_writer: &mut W,
448	block_size: usize,
449) -> Result<BlockRef, Error> {
450	let file_size = dir.encoded_size() as u64;
451	let mut writer = NodeWriter::new_dir(service_id, block_writer, file_size, block_size)?;
452	let mut output = FallibleOutput { writer: &mut writer, error: None };
453	dir.encode_to(&mut output);
454	if let Some(e) = output.error {
455		return Err(e);
456	}
457	let (main_block_ref, _writer) = writer.finish()?;
458	Ok(main_block_ref)
459}
460
461/// Recurisvely copy the directory from the host file system to the block storage.
462///
463/// Returns the main block hash of the destination directory.
464pub fn copy_dir_in<F: HostFileRead, R: HostDirRead<F>, W: WriteBlock>(
465	host_dir_reader: R,
466	service_id: ServiceId,
467	block_writer: &mut W,
468	block_size: usize,
469) -> Result<BlockRef, Error> {
470	let mut dir_stack = Vec::new();
471	let mut queue = VecDeque::new();
472	let mut last_dir_ref = BlockRef { service_id: 0, hash: Hash::default() };
473	let mut visited_dirs = VecSet::new();
474	queue.push_back((host_dir_reader, FileName(Default::default()), usize::MAX));
475	while let Some((mut host_dir_reader, dir_name, parent_dir_index)) = queue.pop_front() {
476		let mut files = VecMap::new();
477		let mut subdirs = false;
478		while let Some(entry) = host_dir_reader.next_entry() {
479			let entry = entry?;
480			match entry.kind {
481				NodeKind::File => {
482					let mut file = host_dir_reader.open_file(&entry.file_name)?;
483					let block_ref = copy_file_in(&mut file, service_id, block_writer, block_size)?;
484					files.insert(entry.file_name, block_ref);
485				},
486				NodeKind::Dir => {
487					let (another_dir_reader, dir_id) =
488						host_dir_reader.open_dir(&entry.file_name)?;
489					let visited = match dir_id {
490						Some(dir_id) => !visited_dirs.insert(dir_id),
491						None => false,
492					};
493					if visited {
494						return Err(Error::Loop);
495					}
496					queue.push_back((another_dir_reader, entry.file_name, dir_stack.len()));
497					subdirs = true;
498				},
499			}
500		}
501		if subdirs {
502			dir_stack.push((files, dir_name, parent_dir_index));
503			continue;
504		}
505		// Don't use stack for directories that don't contain other directories.
506		let dir = Dir(files);
507		last_dir_ref = create_dir(&dir, service_id, block_writer, block_size)?;
508		if parent_dir_index == usize::MAX {
509			continue;
510		}
511		dir_stack[parent_dir_index].0.insert(dir_name, last_dir_ref);
512	}
513	while let Some((files, dir_name, parent_dir_index)) = dir_stack.pop() {
514		let dir = Dir(files);
515		last_dir_ref = create_dir(&dir, service_id, block_writer, block_size)?;
516		if parent_dir_index == usize::MAX {
517			continue;
518		}
519		dir_stack[parent_dir_index].0.insert(dir_name, last_dir_ref);
520	}
521	Ok(last_dir_ref)
522}
523
524/// Recursively copy the directory referenced by `main_block_ref` from the block storage to the
525/// host file system.
526pub fn copy_dir_out<R: ReadBlock, W: HostDirWrite>(
527	main_block_ref: &BlockRef,
528	block_reader: &mut R,
529	host_dir_writer: W,
530) -> Result<(), Error> {
531	let reader = NodeReader::new(main_block_ref, &mut *block_reader)?;
532	if reader.file.main_block.kind != NodeKind::Dir {
533		// Not a directory.
534		return Err(Error::Node);
535	}
536	do_copy_dir_out(reader, host_dir_writer)
537}
538
539fn do_copy_dir_out<R: ReadBlock, W: HostDirWrite>(
540	mut node_reader: NodeReader<R>,
541	host_dir_writer: W,
542) -> Result<(), Error> {
543	let mut queue = VecDeque::new();
544	let dir = Dir::decode(&mut node_reader).map_err(|_| Error::Io)?;
545	let mut block_reader = node_reader.into_inner();
546	queue.push_back((dir, host_dir_writer));
547	while let Some((dir, mut host_dir_writer)) = queue.pop_front() {
548		for (file_name, hash) in dir.0.iter() {
549			let mut reader = NodeReader::new(hash, &mut block_reader)?;
550			match reader.file.main_block.kind {
551				NodeKind::File => {
552					let mut file = host_dir_writer.create_file(file_name)?;
553					do_copy_file_out(&mut reader, &mut file)?;
554				},
555				NodeKind::Dir => {
556					let another_dir = Dir::decode(&mut reader).map_err(|_| Error::Io)?;
557					let another_dir_writer = host_dir_writer.create_dir(file_name)?;
558					queue.push_back((another_dir, another_dir_writer));
559				},
560			}
561		}
562	}
563	Ok(())
564}
565
566/// Recursively traverses file system nodes.
567pub struct NodeIter<R: ReadBlock> {
568	queue: VecDeque<BlockRef>,
569	reader: R,
570}
571
572impl<R: ReadBlock> NodeIter<R> {
573	pub fn new(main_block_ref: BlockRef, reader: R) -> Self {
574		let mut queue = VecDeque::new();
575		queue.push_back(main_block_ref);
576		Self { queue, reader }
577	}
578}
579
580impl<R: ReadBlock> Iterator for NodeIter<R> {
581	type Item = Result<(BlockRef, File), Error>;
582
583	fn next(&mut self) -> Option<Self::Item> {
584		macro_rules! check {
585			($body: expr) => {
586				match $body {
587					Ok(ret) => ret,
588					Err(e) => return Some(Err(e)),
589				}
590			};
591		}
592		let hash = self.queue.pop_front()?;
593		let mut reader = check!(NodeReader::new(&hash, &mut self.reader));
594		match reader.file.main_block.kind {
595			NodeKind::File => Some(Ok((hash, reader.into_file()))),
596			NodeKind::Dir => {
597				let dir = check!(Dir::decode(&mut reader).map_err(|_| Error::Io));
598				for (_name, hash) in dir.0.into_iter() {
599					self.queue.push_back(hash);
600				}
601				check!(reader.seek(0));
602				Some(Ok((hash, reader.into_file())))
603			},
604		}
605	}
606}
607
608/// Copy the file or directory (recursively) referenced by `main_block_ref` from the block storage
609/// to the host file system.
610pub fn copy_out<R: ReadBlock, W: HostWrite>(
611	main_block_ref: &BlockRef,
612	block_reader: &mut R,
613	host_writer: W,
614) -> Result<(), Error> {
615	let mut reader = NodeReader::new(main_block_ref, block_reader)?;
616	match reader.file.main_block.kind {
617		NodeKind::File => {
618			let mut file_writer = host_writer.into_file_writer()?;
619			do_copy_file_out(&mut reader, &mut file_writer)?
620		},
621		NodeKind::Dir => do_copy_dir_out(reader, host_writer.into_dir_writer()?)?,
622	}
623	Ok(())
624}
625
626/// Fully read the file referenced by `main_block_ref`.
627pub fn read<R: ReadBlock>(
628	main_block_ref: &BlockRef,
629	block_reader: &mut R,
630) -> Result<Vec<u8>, Error> {
631	let mut reader = NodeReader::new(main_block_ref, block_reader)?;
632	let mut buf = Vec::with_capacity(reader.file().main_block().file_size() as usize);
633	reader.read_to_end(&mut buf)?;
634	Ok(buf)
635}
636
637/// Resolve `path` into main block reference.
638///
639/// `root_dir_ref` refers to the root directory, `current_dir` is the current working directory
640/// path. Panics if `current_dir` is not absolute.
641pub fn resolve_path<R: ReadBlock>(
642	root_dir_ref: BlockRef,
643	current_dir: &CStr,
644	path: &CStr,
645	block_reader: &mut R,
646) -> Result<BlockRef, Error> {
647	assert!(
648		current_dir.to_bytes().is_empty() || matches!(current_dir.to_bytes(), [b'/', ..]),
649		"Current directory path must be absolute: {current_dir:?}"
650	);
651	let mut dir_stack = Vec::new();
652	let mut pending_ref = root_dir_ref;
653	let cwd_components = match path.to_bytes() {
654		[b'/', ..] => c"",
655		_ => current_dir,
656	}
657	.to_bytes()
658	.split(|b| *b == b'/');
659	let path_components = path.to_bytes().split(|b| *b == b'/');
660	for component in cwd_components.chain(path_components) {
661		match component {
662			b"." | b"" => {},
663			b".." =>
664				if let Some(dir_ref) = dir_stack.pop() {
665					pending_ref = dir_ref;
666				},
667			name => {
668				let dir_ref = pending_ref;
669				let dir = Dir::open(&dir_ref, block_reader)?;
670				pending_ref = dir.0.get(name).cloned().ok_or(Error::Path)?;
671				dir_stack.push(dir_ref);
672			},
673		}
674	}
675	Ok(pending_ref)
676}