1use alloc::{
16 collections::BTreeMap,
17 format,
18 string::{String, ToString},
19 sync::Arc,
20 vec::Vec,
21};
22
23use ax_fs_vfs::{
24 VfsDirEntry, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodePerm, VfsNodeRef, VfsNodeType, VfsOps,
25 VfsResult,
26};
27use rsext4::{
28 Ext4Error, Ext4FileSystem as Rsext4FileSystem, Ext4Result, Ext4Timestamp, Jbd2Dev,
29 api::{OpenFile, fs_mount, lseek, open, read_at},
30 dir::{get_inode_with_num, mkdir},
31 entries::classic_dir::list_entries,
32 file::{delete_dir, mkfile, mv, truncate, unlink, write_file},
33 loopfile::resolve_inode_block_allextend,
34};
35use spin::Mutex;
36
37use crate::dev::{Disk, Partition};
38
39pub const BLOCK_SIZE: usize = 4096;
41
42#[allow(dead_code)]
44pub struct Ext4FileSystem {
45 inner: Arc<Mutex<Jbd2Dev<Disk>>>,
46 fs: Arc<Mutex<Rsext4FileSystem>>,
47}
48
49pub struct Ext4FileSystemPartition {
51 inner: Arc<Mutex<Jbd2Dev<Partition>>>,
52 fs: Arc<Mutex<Rsext4FileSystem>>,
53}
54
55unsafe impl Sync for Ext4FileSystem {}
56unsafe impl Send for Ext4FileSystem {}
57
58unsafe impl Sync for Ext4FileSystemPartition {}
59unsafe impl Send for Ext4FileSystemPartition {}
60
61impl Ext4FileSystem {
62 #[allow(dead_code)]
64 pub fn new(disk: Disk) -> Self {
65 info!(
66 "Got Disk size:{}, position:{}",
67 disk.size(),
68 disk.position()
69 );
70 let mut inner = Jbd2Dev::initial_jbd2dev(0, disk, false);
71 let fs = fs_mount(&mut inner).expect("failed to initialize EXT4 filesystem");
72 Self {
73 inner: Arc::new(Mutex::new(inner)),
74 fs: Arc::new(Mutex::new(fs)),
75 }
76 }
77
78 pub fn from_partition(partition: Partition) -> Ext4FileSystemPartition {
80 info!(
81 "Got Partition size:{}, position:{}",
82 partition.size(),
83 partition.position()
84 );
85 let mut inner = Jbd2Dev::initial_jbd2dev(0, partition, false);
86 let fs = fs_mount(&mut inner).expect("failed to initialize EXT4 filesystem on partition");
87 Ext4FileSystemPartition {
88 inner: Arc::new(Mutex::new(inner)),
89 fs: Arc::new(Mutex::new(fs)),
90 }
91 }
92}
93
94impl VfsOps for Ext4FileSystem {
96 fn root_dir(&self) -> VfsNodeRef {
97 debug!("Get root_dir");
98 Arc::new(FileWrapper::new(
99 "/",
100 Ext4Inner::Disk(Arc::clone(&self.inner)),
101 Arc::clone(&self.fs),
102 ))
103 }
104}
105
106impl VfsOps for Ext4FileSystemPartition {
108 fn root_dir(&self) -> VfsNodeRef {
109 debug!("Get root_dir");
110 Arc::new(FileWrapper::new(
111 "/",
112 Ext4Inner::Partition(Arc::clone(&self.inner)),
113 Arc::clone(&self.fs),
114 ))
115 }
116}
117
118#[derive(Clone)]
120pub enum Ext4Inner {
121 Disk(Arc<Mutex<Jbd2Dev<Disk>>>),
123 Partition(Arc<Mutex<Jbd2Dev<Partition>>>),
125}
126
127pub struct FileWrapper {
129 path: String,
130 file: Mutex<Option<OpenFile>>,
131 inner: Ext4Inner,
132 fs: Arc<Mutex<Rsext4FileSystem>>,
133}
134
135unsafe impl Send for FileWrapper {}
136unsafe impl Sync for FileWrapper {}
137
138impl FileWrapper {
139 fn new(path: &str, inner: Ext4Inner, fs: Arc<Mutex<Rsext4FileSystem>>) -> Self {
140 debug!("FileWrapper new {}", path);
141 Self {
142 path: path.to_string(),
143 file: Mutex::new(None),
144 inner,
145 fs,
146 }
147 }
148
149 fn path_deal_with(&self, path: &str) -> String {
150 if path.starts_with('/') {
151 debug!("path_deal_with: {}", path);
152 }
153 let trim_path = path.trim_matches('/');
154 if trim_path.is_empty() || trim_path == "." {
155 return self.path.to_string();
156 }
157
158 if let Some(rest) = trim_path.strip_prefix("./") {
159 return self.path_deal_with(rest);
161 }
162 let rest_p = trim_path.replace("//", "/");
163 if trim_path != rest_p {
164 return self.path_deal_with(&rest_p);
165 }
166
167 let base_path = self.path.trim_end_matches('/');
168 if base_path == "/" {
169 format!("/{}", trim_path)
170 } else {
171 format!("{}/{}", base_path, trim_path)
172 }
173 }
174}
175
176impl VfsNodeOps for FileWrapper {
178 fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
179 let mut fs = self.fs.lock();
180 let perm = VfsNodePerm::from_bits_truncate(0o755);
181 let (_inode_num, inode) = match self.inner {
182 Ext4Inner::Disk(ref inner) => {
183 let mut inner = inner.lock();
184 get_inode_with_num(&mut fs, &mut inner, &self.path)
185 .map_err(|_| VfsError::Io)?
186 .ok_or(VfsError::NotFound)?
187 }
188 Ext4Inner::Partition(ref inner) => {
189 let mut inner = inner.lock();
190 get_inode_with_num(&mut fs, &mut inner, &self.path)
191 .map_err(|_| VfsError::Io)?
192 .ok_or(VfsError::NotFound)?
193 }
194 };
195 let vtype = if inode.is_dir() {
196 VfsNodeType::Dir
197 } else {
198 VfsNodeType::File
199 };
200 let size = inode.size();
201 let blocks = inode.blocks_count();
202
203 trace!(
204 "get_attr of {:?}, size: {}, blocks: {}",
205 self.path, size, blocks
206 );
207
208 Ok(VfsNodeAttr::new(perm, vtype, size, blocks))
209 }
210
211 fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
212 debug!("create {:?} on Ext4fs: {}", ty, path);
213 let fpath = self.path_deal_with(path);
214 if fpath.is_empty() {
215 return Ok(());
216 }
217
218 let mut fs = self.fs.lock();
219 match self.inner {
220 Ext4Inner::Disk(ref inner) => {
221 let mut inner = inner.lock();
222 match ty {
223 VfsNodeType::Dir => {
224 let _ = mkdir(&mut inner, &mut fs, &fpath);
225 }
226 _ => {
227 let _ = mkfile(&mut inner, &mut fs, &fpath, None, None);
228 }
229 }
230 }
231 Ext4Inner::Partition(ref inner) => {
232 let mut inner = inner.lock();
233 match ty {
234 VfsNodeType::Dir => {
235 let _ = mkdir(&mut inner, &mut fs, &fpath);
236 }
237 _ => {
238 let _ = mkfile(&mut inner, &mut fs, &fpath, None, None);
239 }
240 }
241 }
242 }
243 Ok(())
244 }
245
246 fn remove(&self, path: &str) -> VfsResult {
247 debug!("remove ext4fs: {}", path);
248 let fpath = self.path_deal_with(path);
249 assert!(!fpath.is_empty()); let mut fs = self.fs.lock();
252 let (_inode_num, inode) = match self.inner {
253 Ext4Inner::Disk(ref inner) => {
254 let mut inner = inner.lock();
255 get_inode_with_num(&mut fs, &mut inner, &fpath)
256 .map_err(|_| VfsError::Io)?
257 .ok_or(VfsError::NotFound)?
258 }
259 Ext4Inner::Partition(ref inner) => {
260 let mut inner = inner.lock();
261 get_inode_with_num(&mut fs, &mut inner, &fpath)
262 .map_err(|_| VfsError::Io)?
263 .ok_or(VfsError::NotFound)?
264 }
265 };
266
267 match self.inner {
268 Ext4Inner::Disk(ref inner) => {
269 let mut inner = inner.lock();
270 if inode.is_dir() {
271 let _ = delete_dir(&mut fs, &mut inner, &fpath);
272 } else {
273 let _ = unlink(&mut fs, &mut inner, &fpath);
274 }
275 }
276 Ext4Inner::Partition(ref inner) => {
277 let mut inner = inner.lock();
278 if inode.is_dir() {
279 let _ = delete_dir(&mut fs, &mut inner, &fpath);
280 } else {
281 let _ = unlink(&mut fs, &mut inner, &fpath);
282 }
283 }
284 }
285 Ok(())
286 }
287
288 fn parent(&self) -> Option<VfsNodeRef> {
291 let path = &self.path;
292 debug!("Get the parent dir of {}", path);
293 let path = path.trim_end_matches('/').trim_end_matches(|c| c != '/');
294 if !path.is_empty() {
295 return Some(Arc::new(Self::new(
296 path,
297 self.inner.clone(),
298 Arc::clone(&self.fs),
299 )));
300 }
301 None
302 }
303
304 fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
306 let mut fs = self.fs.lock();
307 let (_inode_num, mut inode) = match self.inner {
308 Ext4Inner::Disk(ref inner) => {
309 let mut inner = inner.lock();
310 get_inode_with_num(&mut fs, &mut inner, &self.path)
311 .map_err(|_| VfsError::Io)?
312 .ok_or(VfsError::NotFound)?
313 }
314 Ext4Inner::Partition(ref inner) => {
315 let mut inner = inner.lock();
316 get_inode_with_num(&mut fs, &mut inner, &self.path)
317 .map_err(|_| VfsError::Io)?
318 .ok_or(VfsError::NotFound)?
319 }
320 };
321
322 if !inode.is_dir() {
323 return Err(VfsError::Unsupported);
324 }
325
326 let blocks = match self.inner {
327 Ext4Inner::Disk(ref inner) => {
328 let mut inner = inner.lock();
329 resolve_inode_block_allextend(&mut fs, &mut inner, &mut inode)
330 .map_err(|_| VfsError::Io)?
331 }
332 Ext4Inner::Partition(ref inner) => {
333 let mut inner = inner.lock();
334 resolve_inode_block_allextend(&mut fs, &mut inner, &mut inode)
335 .map_err(|_| VfsError::Io)?
336 }
337 };
338
339 let mut data = Vec::new();
340 for (_, phys_block) in blocks {
341 let cached = match self.inner {
342 Ext4Inner::Disk(ref inner) => {
343 let mut inner = inner.lock();
344 fs.datablock_cache
345 .get_or_load(&mut inner, phys_block)
346 .map_err(|_| VfsError::Io)?
347 }
348 Ext4Inner::Partition(ref inner) => {
349 let mut inner = inner.lock();
350 fs.datablock_cache
351 .get_or_load(&mut inner, phys_block)
352 .map_err(|_| VfsError::Io)?
353 }
354 };
355 data.extend_from_slice(&cached.data);
356 }
357
358 let entries = list_entries(&data);
359 let mut unique = BTreeMap::new();
360 for entry in entries {
361 if let Some(name) = entry.name_str()
362 && name != "."
363 && name != ".."
364 {
365 unique.insert(name.to_string(), entry.file_type);
366 }
367 }
368 let unique_vec: Vec<_> = unique.into_iter().collect();
369 let mut count = 0;
370 for (name, file_type) in unique_vec.iter().skip(start_idx) {
371 if count >= dirents.len() {
372 break;
373 }
374 let ty = match *file_type {
375 2 => VfsNodeType::Dir,
376 _ => VfsNodeType::File,
377 };
378 dirents[count] = VfsDirEntry::new(name, ty);
379 count += 1;
380 }
381 Ok(count)
382 }
383
384 fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
387 trace!("lookup ext4fs: {}, {}", self.path, path);
388 let fpath = self.path_deal_with(path);
389 if fpath.is_empty() {
390 return Ok(self.clone());
391 }
392
393 let mut fs = self.fs.lock();
394 let exists = match self.inner {
395 Ext4Inner::Disk(ref inner) => {
396 let mut inner = inner.lock();
397 get_inode_with_num(&mut fs, &mut inner, &fpath)
398 .map_err(|_| VfsError::Io)?
399 .is_some()
400 }
401 Ext4Inner::Partition(ref inner) => {
402 let mut inner = inner.lock();
403 get_inode_with_num(&mut fs, &mut inner, &fpath)
404 .map_err(|_| VfsError::Io)?
405 .is_some()
406 }
407 };
408
409 if exists {
410 Ok(Arc::new(Self::new(
411 &fpath,
412 self.inner.clone(),
413 Arc::clone(&self.fs),
414 )))
415 } else {
416 Err(VfsError::NotFound)
417 }
418 }
419
420 fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult<usize> {
421 let mut file_guard = self.file.lock();
422 if file_guard.is_none() {
423 let mut fs = self.fs.lock();
424 *file_guard = match self.inner {
425 Ext4Inner::Disk(ref inner) => {
426 let mut inner = inner.lock();
427 open(&mut inner, &mut fs, &self.path, false).ok()
428 }
429 Ext4Inner::Partition(ref inner) => {
430 let mut inner = inner.lock();
431 open(&mut inner, &mut fs, &self.path, false).ok()
432 }
433 };
434 }
435
436 if let Some(ref mut file) = *file_guard {
437 let mut fs = self.fs.lock();
438 let _ = lseek(file, offset);
439 let data = match self.inner {
440 Ext4Inner::Disk(ref inner) => {
441 let mut inner = inner.lock();
442 read_at(&mut inner, &mut fs, file, buf.len()).map_err(|_| VfsError::Io)?
443 }
444 Ext4Inner::Partition(ref inner) => {
445 let mut inner = inner.lock();
446 read_at(&mut inner, &mut fs, file, buf.len()).map_err(|_| VfsError::Io)?
447 }
448 };
449 let len = data.len().min(buf.len());
450 buf[..len].copy_from_slice(&data[..len]);
451 Ok(len)
452 } else {
453 Err(VfsError::NotFound)
454 }
455 }
456
457 fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult<usize> {
458 let mut fs = self.fs.lock();
459 match self.inner {
460 Ext4Inner::Disk(ref inner) => {
461 let mut inner = inner.lock();
462 write_file(&mut inner, &mut fs, &self.path, offset, buf)
463 .map_err(|_| VfsError::Io)?;
464 }
465 Ext4Inner::Partition(ref inner) => {
466 let mut inner = inner.lock();
467 write_file(&mut inner, &mut fs, &self.path, offset, buf)
468 .map_err(|_| VfsError::Io)?;
469 }
470 };
471 Ok(buf.len())
472 }
473
474 fn truncate(&self, size: u64) -> VfsResult {
475 let mut fs = self.fs.lock();
476 match self.inner {
477 Ext4Inner::Disk(ref inner) => {
478 let mut inner = inner.lock();
479 let _ = truncate(&mut inner, &mut fs, &self.path, size);
480 }
481 Ext4Inner::Partition(ref inner) => {
482 let mut inner = inner.lock();
483 let _ = truncate(&mut inner, &mut fs, &self.path, size);
484 }
485 }
486 Ok(())
487 }
488
489 fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult {
490 debug!("rename from {} to {}", src_path, dst_path);
491
492 let src_fpath = self.path_deal_with(src_path);
493 let dst_fpath = self.path_deal_with(dst_path);
494
495 let mut fs = self.fs.lock();
496 match self.inner {
497 Ext4Inner::Disk(ref inner) => {
498 let mut inner = inner.lock();
499 let _ = mv(&mut fs, &mut inner, &src_fpath, &dst_fpath);
500 }
501 Ext4Inner::Partition(ref inner) => {
502 let mut inner = inner.lock();
503 let _ = mv(&mut fs, &mut inner, &src_fpath, &dst_fpath);
504 }
505 }
506 Ok(())
507 }
508
509 fn as_any(&self) -> &dyn core::any::Any {
510 self as &dyn core::any::Any
511 }
512}
513
514impl Drop for FileWrapper {
515 fn drop(&mut self) {
516 debug!("Drop struct FileWrapper {:?}", self.path);
517 }
519}
520
521impl rsext4::BlockDevice for Disk {
522 fn write(
523 &mut self,
524 buffer: &[u8],
525 block_id: rsext4::bmalloc::AbsoluteBN,
526 count: u32,
527 ) -> Ext4Result<()> {
528 self.set_position(block_id.raw() * BLOCK_SIZE as u64);
530 let mut total_written = 0;
531 let to_write = count as usize * BLOCK_SIZE;
532
533 while total_written < to_write {
534 let remaining = &buffer[total_written..];
535 let written = self.write_one(remaining).map_err(|_| Ext4Error::io())?;
536 total_written += written;
537 }
538
539 Ok(())
540 }
541
542 fn read(
543 &mut self,
544 buffer: &mut [u8],
545 block_id: rsext4::bmalloc::AbsoluteBN,
546 count: u32,
547 ) -> Ext4Result<()> {
548 self.set_position(block_id.raw() * BLOCK_SIZE as u64);
549 let mut total_read = 0;
550 let to_read = count as usize * BLOCK_SIZE;
551
552 while total_read < to_read {
553 let remaining = &mut buffer[total_read..];
554 let read = self.read_one(remaining).map_err(|_| Ext4Error::io())?;
555 total_read += read;
556 }
557
558 Ok(())
559 }
560
561 fn open(&mut self) -> Ext4Result<()> {
562 Ok(())
563 }
564
565 fn close(&mut self) -> Ext4Result<()> {
566 Ok(())
567 }
568
569 fn total_blocks(&self) -> u64 {
570 self.size() / BLOCK_SIZE as u64
572 }
573
574 fn current_time(&self) -> Ext4Result<Ext4Timestamp> {
575 let now = ax_hal::time::wall_time();
576 let sec =
577 i64::try_from(now.as_secs()).map_err(|_| Ext4Error::from(rsext4::Errno::EOVERFLOW))?;
578 Ok(Ext4Timestamp::new(sec, now.subsec_nanos()))
579 }
580}
581
582impl rsext4::BlockDevice for Partition {
583 fn write(
584 &mut self,
585 buffer: &[u8],
586 block_id: rsext4::bmalloc::AbsoluteBN,
587 count: u32,
588 ) -> Ext4Result<()> {
589 self.set_position(block_id.raw() * BLOCK_SIZE as u64);
590 let mut total_written = 0;
591 let to_write = count as usize * BLOCK_SIZE;
592
593 while total_written < to_write {
594 let remaining = &buffer[total_written..];
595 let written = self.write_one(remaining).map_err(|_| Ext4Error::io())?;
596 total_written += written;
597 }
598
599 Ok(())
600 }
601
602 fn read(
603 &mut self,
604 buffer: &mut [u8],
605 block_id: rsext4::bmalloc::AbsoluteBN,
606 count: u32,
607 ) -> Ext4Result<()> {
608 self.set_position(block_id.raw() * BLOCK_SIZE as u64);
609 let mut total_read = 0;
610 let to_read = count as usize * BLOCK_SIZE;
611
612 while total_read < to_read {
613 let remaining = &mut buffer[total_read..];
614 let read = self.read_one(remaining).map_err(|_| Ext4Error::io())?;
615 total_read += read;
616 }
617
618 Ok(())
619 }
620
621 fn open(&mut self) -> Ext4Result<()> {
622 Ok(())
623 }
624
625 fn close(&mut self) -> Ext4Result<()> {
626 Ok(())
627 }
628
629 fn total_blocks(&self) -> u64 {
630 self.size() / BLOCK_SIZE as u64
632 }
633
634 fn current_time(&self) -> Ext4Result<Ext4Timestamp> {
635 let now = ax_hal::time::wall_time();
636 let sec =
637 i64::try_from(now.as_secs()).map_err(|_| Ext4Error::from(rsext4::Errno::EOVERFLOW))?;
638 Ok(Ext4Timestamp::new(sec, now.subsec_nanos()))
639 }
640}