1#[macro_use]
32extern crate log;
33#[macro_use]
34extern crate bitflags;
35#[macro_use]
36extern crate nydus_api;
37#[macro_use]
38extern crate nydus_storage as storage;
39
40use std::any::Any;
41use std::borrow::Cow;
42use std::fmt::Debug;
43use std::fs::File;
44use std::io::{BufWriter, Error, Read, Result, Seek, SeekFrom, Write};
45use std::os::unix::io::AsRawFd;
46use std::path::{Path, PathBuf};
47use std::sync::Arc;
48
49use crate::metadata::{RafsInodeExt, RafsSuper};
50
51#[cfg(feature = "virtio-fs")]
52pub mod blobfs;
53pub mod fs;
54pub mod metadata;
55#[cfg(test)]
56pub mod mock;
57
58#[derive(thiserror::Error, Debug)]
60pub enum RafsError {
61 #[error("Operation is not supported.")]
62 Unsupported,
63 #[error("Rafs is not initialized.")]
64 Uninitialized,
65 #[error("Rafs is already mounted.")]
66 AlreadyMounted,
67 #[error("Failed to read metadata: {0}`")]
68 ReadMetadata(Error, String),
69 #[error("Failed to load config: {0}`")]
70 LoadConfig(Error),
71 #[error("Failed to parse config: {0}`")]
72 ParseConfig(#[source] serde_json::Error),
73 #[error("Failed to create swap backend: {0}`")]
74 SwapBackend(Error),
75 #[error("Failed to fill superBlock: {0}`")]
76 FillSuperBlock(Error),
77 #[error("Failed to create device: {0}`")]
78 CreateDevice(Error),
79 #[error("Failed to prefetch data: {0}`")]
80 Prefetch(String),
81 #[error("Failed to configure device: {0}`")]
82 Configure(String),
83 #[error("Incompatible RAFS version: `{0}`")]
84 Incompatible(u16),
85 #[error("Illegal meta struct, type is `{0:?}` and content is `{1}`")]
86 IllegalMetaStruct(MetaType, String),
87 #[error("Invalid image data")]
88 InvalidImageData,
89}
90
91#[derive(Debug)]
92pub enum MetaType {
93 Regular,
94 Dir,
95 Symlink,
96}
97
98pub type RafsResult<T> = std::result::Result<T, RafsError>;
100
101pub type RafsIoReader = Box<dyn RafsIoRead>;
103
104pub trait RafsIoRead: Read + AsRawFd + Seek + Send {}
106
107impl RafsIoRead for File {}
108
109pub type RafsIoWriter = Box<dyn RafsIoWrite>;
111
112pub trait RafsIoWrite: Write + Seek + 'static {
114 fn as_any(&self) -> &dyn Any;
115
116 fn validate_alignment(&mut self, size: usize, alignment: usize) -> Result<usize> {
117 if alignment != 0 {
118 let cur = self.stream_position()?;
119
120 if (size & (alignment - 1) != 0) || (cur & (alignment as u64 - 1) != 0) {
121 return Err(einval!("unaligned data"));
122 }
123 }
124
125 Ok(size)
126 }
127
128 fn write_padding(&mut self, size: usize) -> Result<()> {
130 if size > WRITE_PADDING_DATA.len() {
131 return Err(einval!("invalid padding size"));
132 }
133 self.write_all(&WRITE_PADDING_DATA[0..size])
134 }
135
136 fn seek_to_end(&mut self) -> Result<u64> {
138 self.seek(SeekFrom::End(0)).map_err(|e| {
139 error!("Seeking to end fails, {}", e);
140 e
141 })
142 }
143
144 fn seek_offset(&mut self, offset: u64) -> Result<u64> {
146 self.seek(SeekFrom::Start(offset)).map_err(|e| {
147 error!("Seeking to offset {} from start fails, {}", offset, e);
148 e
149 })
150 }
151
152 fn seek_current(&mut self, offset: i64) -> Result<u64> {
154 self.seek(SeekFrom::Current(offset))
155 }
156
157 fn finalize(&mut self, _name: Option<String>) -> anyhow::Result<()> {
159 Ok(())
160 }
161
162 fn as_bytes(&mut self) -> std::io::Result<Cow<'_, [u8]>> {
166 unimplemented!()
167 }
168}
169
170impl RafsIoWrite for File {
171 fn as_any(&self) -> &dyn Any {
172 self
173 }
174}
175
176impl RafsIoWrite for BufWriter<File> {
180 fn as_any(&self) -> &dyn Any {
181 self
182 }
183}
184
185const WRITE_PADDING_DATA: [u8; 64] = [0u8; 64];
186
187impl dyn RafsIoRead {
188 pub fn seek_to_next_aligned(&mut self, last_read_len: usize, alignment: usize) -> Result<u64> {
190 let suffix = last_read_len & (alignment - 1);
191 let offset = if suffix == 0 { 0 } else { alignment - suffix };
192
193 self.seek(SeekFrom::Current(offset as i64)).map_err(|e| {
194 error!("Seeking to offset {} from current fails, {}", offset, e);
195 e
196 })
197 }
198
199 pub fn seek_plus_offset(&mut self, plus_offset: i64) -> Result<u64> {
201 self.seek(SeekFrom::Current(plus_offset)).map_err(|e| {
203 error!(
204 "Seeking to offset {} from current fails, {}",
205 plus_offset, e
206 );
207 e
208 })
209 }
210
211 pub fn seek_to_offset(&mut self, offset: u64) -> Result<u64> {
213 self.seek(SeekFrom::Start(offset)).map_err(|e| {
214 error!("Seeking to offset {} from start fails, {}", offset, e);
215 e
216 })
217 }
218
219 pub fn seek_to_end(&mut self, offset: i64) -> Result<u64> {
221 self.seek(SeekFrom::End(offset)).map_err(|e| {
222 error!("Seeking to end fails, {}", e);
223 e
224 })
225 }
226
227 pub fn from_file(path: impl AsRef<Path>) -> RafsResult<RafsIoReader> {
229 let f = File::open(&path).map_err(|e| {
230 RafsError::ReadMetadata(e, path.as_ref().to_string_lossy().into_owned())
231 })?;
232
233 Ok(Box::new(f))
234 }
235}
236
237pub struct RafsIterator<'a> {
239 _rs: &'a RafsSuper,
240 cursor_stack: Vec<(Arc<dyn RafsInodeExt>, PathBuf)>,
241}
242
243impl<'a> RafsIterator<'a> {
244 pub fn new(rs: &'a RafsSuper) -> Self {
246 let cursor_stack = match rs.get_extended_inode(rs.superblock.root_ino(), false) {
247 Ok(node) => {
248 let path = PathBuf::from("/");
249 vec![(node, path)]
250 }
251 Err(e) => {
252 error!(
253 "failed to get root inode from the bootstrap {}, damaged or malicious file?",
254 e
255 );
256 vec![]
257 }
258 };
259
260 RafsIterator {
261 _rs: rs,
262 cursor_stack,
263 }
264 }
265}
266
267impl Iterator for RafsIterator<'_> {
268 type Item = (Arc<dyn RafsInodeExt>, PathBuf);
269
270 fn next(&mut self) -> Option<Self::Item> {
271 let (node, path) = self.cursor_stack.pop()?;
272 if node.is_dir() {
273 let children = 0..node.get_child_count();
274 for idx in children.rev() {
275 if let Ok(child) = node.get_child_by_index(idx) {
276 let child_path = path.join(child.name());
277 self.cursor_stack.push((child, child_path));
278 } else {
279 error!(
280 "failed to get child inode from the bootstrap, damaged or malicious file?"
281 );
282 }
283 }
284 }
285 Some((node, path))
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292 use crate::metadata::RafsMode;
293 use std::fs::OpenOptions;
294 use vmm_sys_util::tempfile::TempFile;
295
296 #[test]
297 fn test_rafs_io_writer() {
298 let mut file = TempFile::new().unwrap().into_file();
299
300 assert!(file.validate_alignment(2, 8).is_err());
301 assert!(file.validate_alignment(7, 8).is_err());
302 assert!(file.validate_alignment(9, 8).is_err());
303 assert!(file.validate_alignment(8, 8).is_ok());
304
305 file.write_all(&[0x0u8; 7]).unwrap();
306 assert!(file.validate_alignment(8, 8).is_err());
307 {
308 let obj: &mut dyn RafsIoWrite = &mut file;
309 obj.write_padding(1).unwrap();
310 }
311 assert!(file.validate_alignment(8, 8).is_ok());
312 file.write_all(&[0x0u8; 1]).unwrap();
313 assert!(file.validate_alignment(8, 8).is_err());
314
315 let obj: &mut dyn RafsIoRead = &mut file;
316 assert_eq!(obj.seek_to_offset(0).unwrap(), 0);
317 assert_eq!(obj.seek_plus_offset(7).unwrap(), 7);
318 assert_eq!(obj.seek_to_next_aligned(7, 8).unwrap(), 8);
319 assert_eq!(obj.seek_plus_offset(7).unwrap(), 15);
320 }
321
322 #[test]
323 fn test_rafs_iterator() {
324 let root_dir = &std::env::var("CARGO_MANIFEST_DIR").expect("$CARGO_MANIFEST_DIR");
325 let path = PathBuf::from(root_dir).join("../tests/texture/bootstrap/rafs-v5.boot");
326 let bootstrap = OpenOptions::new()
327 .read(true)
328 .write(false)
329 .open(path)
330 .unwrap();
331 let mut rs = RafsSuper {
332 mode: RafsMode::Direct,
333 validate_digest: false,
334 ..Default::default()
335 };
336 rs.load(&mut (Box::new(bootstrap) as RafsIoReader)).unwrap();
337 let iter = RafsIterator::new(&rs);
338
339 let mut last = false;
340 for (idx, (_node, path)) in iter.enumerate() {
341 assert!(!last);
342 if idx == 1 {
343 assert_eq!(path, PathBuf::from("/bin"));
344 } else if idx == 2 {
345 assert_eq!(path, PathBuf::from("/boot"));
346 } else if idx == 3 {
347 assert_eq!(path, PathBuf::from("/dev"));
348 } else if idx == 10 {
349 assert_eq!(path, PathBuf::from("/etc/DIR_COLORS.256color"));
350 } else if idx == 11 {
351 assert_eq!(path, PathBuf::from("/etc/DIR_COLORS.lightbgcolor"));
352 } else if path == PathBuf::from("/var/yp") {
353 last = true;
354 }
355 }
356 assert!(last);
357 }
358}