ext4.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
  2. use crate::{
  3. io::{Buffer, ByteBuffer, Stream},
  4. kernel::{
  5. block::BlockDevice,
  6. constants::{EEXIST, EINVAL, EIO, ENOSYS, S_IFDIR, S_IFREG},
  7. timer::Instant,
  8. vfs::{
  9. dentry::{dcache, Dentry},
  10. inode::{
  11. define_struct_inode, AtomicNlink, Ino, Inode, InodeData, Mode, RenameData,
  12. WriteOffset,
  13. },
  14. mount::{register_filesystem, Mount, MountCreator},
  15. s_isdir, s_isreg,
  16. vfs::Vfs,
  17. DevId, FsContext,
  18. },
  19. },
  20. path::Path,
  21. prelude::*,
  22. };
  23. use alloc::{
  24. collections::btree_map::{BTreeMap, Entry},
  25. sync::{Arc, Weak},
  26. };
  27. use another_ext4::{
  28. Block, BlockDevice as Ext4BlockDeviceTrait, Ext4, FileType, InodeMode, PBlockId,
  29. };
  30. use eonix_runtime::task::Task;
  31. use eonix_sync::RwLock;
  32. pub struct Ext4BlockDevice {
  33. device: Arc<BlockDevice>,
  34. }
  35. impl Ext4BlockDevice {
  36. pub fn new(device: Arc<BlockDevice>) -> Self {
  37. Self { device }
  38. }
  39. }
  40. impl Ext4BlockDeviceTrait for Ext4BlockDevice {
  41. fn read_block(&self, block_id: PBlockId) -> Block {
  42. let mut buffer = [0u8; 4096];
  43. let mut byte_buffer = ByteBuffer::new(buffer.as_mut_slice());
  44. let _ = self
  45. .device
  46. .read_some((block_id as usize) * 4096, &mut byte_buffer)
  47. .expect("Failed to read from block device");
  48. Block {
  49. id: block_id,
  50. data: buffer,
  51. }
  52. }
  53. fn write_block(&self, block: &another_ext4::Block) {
  54. let _ = self
  55. .device
  56. .write_some((block.id as usize) * 4096, &block.data);
  57. }
  58. }
  59. impl_any!(Ext4Fs);
  60. struct Ext4Fs {
  61. inner: Ext4,
  62. device: Arc<BlockDevice>,
  63. icache: RwLock<BTreeMap<Ino, Ext4Inode>>,
  64. }
  65. impl Vfs for Ext4Fs {
  66. fn io_blksize(&self) -> usize {
  67. 4096
  68. }
  69. fn fs_devid(&self) -> DevId {
  70. self.device.devid()
  71. }
  72. fn is_read_only(&self) -> bool {
  73. true
  74. }
  75. }
  76. impl Ext4Fs {
  77. fn try_get(&self, icache: &BTreeMap<Ino, Ext4Inode>, ino: u64) -> Option<Arc<dyn Inode>> {
  78. icache.get(&ino).cloned().map(Ext4Inode::into_inner)
  79. }
  80. fn modify_inode_stat(&self, ino: u32, size: Option<u64>, mtime: u32) {
  81. let _ = self
  82. .inner
  83. .setattr(ino, None, None, None, size, None, Some(mtime), None, None);
  84. }
  85. fn create_inode_stat(&self, parent: u32, child: u32, mtime: u32) {
  86. let _ = self.inner.setattr(
  87. parent,
  88. None,
  89. None,
  90. None,
  91. None,
  92. None,
  93. Some(mtime),
  94. None,
  95. None,
  96. );
  97. let _ = self
  98. .inner
  99. .setattr(child, None, None, None, None, None, Some(mtime), None, None);
  100. }
  101. fn chmod_stat(&self, ino: u32, new_mode: u16, ctime: u32) {
  102. let _ = self.inner.setattr(
  103. ino,
  104. Some(InodeMode::from_bits_retain(new_mode.try_into().unwrap())),
  105. None,
  106. None,
  107. None,
  108. None,
  109. None,
  110. Some(ctime),
  111. None,
  112. );
  113. }
  114. fn get_or_insert(
  115. &self,
  116. icache: &mut BTreeMap<Ino, Ext4Inode>,
  117. mut idata: InodeData,
  118. ) -> Arc<dyn Inode> {
  119. match icache.entry(idata.ino) {
  120. Entry::Occupied(occupied) => occupied.get().clone().into_inner(),
  121. Entry::Vacant(vacant) => {
  122. let mode = *idata.mode.get_mut();
  123. if s_isreg(mode) {
  124. vacant
  125. .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
  126. .clone()
  127. .into_inner()
  128. } else if s_isdir(mode) {
  129. vacant
  130. .insert(Ext4Inode::Dir(Arc::new(DirInode { idata })))
  131. .clone()
  132. .into_inner()
  133. } else {
  134. println_warn!("ext4: Unsupported inode type: {mode:#o}");
  135. vacant
  136. .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
  137. .clone()
  138. .into_inner()
  139. }
  140. }
  141. }
  142. }
  143. }
  144. impl Ext4Fs {
  145. pub fn create(device: Arc<BlockDevice>) -> KResult<(Arc<Self>, Arc<dyn Inode>)> {
  146. let ext4_device = Ext4BlockDevice::new(device.clone());
  147. let ext4 = Ext4::load(Arc::new(ext4_device)).unwrap();
  148. let ext4fs = Arc::new(Self {
  149. inner: ext4,
  150. device,
  151. icache: RwLock::new(BTreeMap::new()),
  152. });
  153. let root_inode = {
  154. let mut icache = Task::block_on(ext4fs.icache.write());
  155. let root_inode = ext4fs.inner.read_root_inode();
  156. ext4fs.get_or_insert(
  157. &mut icache,
  158. InodeData {
  159. ino: root_inode.id as Ino,
  160. size: AtomicU64::new(root_inode.inode.size()),
  161. nlink: AtomicNlink::new(root_inode.inode.link_count() as u64),
  162. uid: AtomicU32::new(root_inode.inode.uid()),
  163. gid: AtomicU32::new(root_inode.inode.gid()),
  164. mode: AtomicU32::new(root_inode.inode.mode().bits() as u32),
  165. atime: Spin::new(Instant::new(
  166. root_inode.inode.atime() as _,
  167. root_inode.inode.atime_extra() as _,
  168. )),
  169. ctime: Spin::new(Instant::new(
  170. root_inode.inode.ctime() as _,
  171. root_inode.inode.ctime_extra() as _,
  172. )),
  173. mtime: Spin::new(Instant::new(
  174. root_inode.inode.mtime() as _,
  175. root_inode.inode.mtime_extra() as _,
  176. )),
  177. rwsem: RwLock::new(()),
  178. vfs: Arc::downgrade(&ext4fs) as _,
  179. },
  180. )
  181. };
  182. Ok((ext4fs, root_inode))
  183. }
  184. }
  185. #[derive(Clone)]
  186. enum Ext4Inode {
  187. File(Arc<FileInode>),
  188. Dir(Arc<DirInode>),
  189. }
  190. impl Ext4Inode {
  191. fn into_inner(self) -> Arc<dyn Inode> {
  192. match self {
  193. Ext4Inode::File(inode) => inode,
  194. Ext4Inode::Dir(inode) => inode,
  195. }
  196. }
  197. }
  198. define_struct_inode! {
  199. struct FileInode;
  200. }
  201. define_struct_inode! {
  202. struct DirInode;
  203. }
  204. impl FileInode {
  205. pub fn new(ino: Ino, vfs: Weak<dyn Vfs>, mode: Mode) -> Arc<Self> {
  206. Arc::new_cyclic(|_| FileInode {
  207. idata: {
  208. let inode_data = InodeData::new(ino, vfs);
  209. inode_data
  210. .mode
  211. .store(S_IFREG | (mode & 0o777), Ordering::Relaxed);
  212. inode_data.nlink.store(1, Ordering::Relaxed);
  213. inode_data
  214. },
  215. })
  216. }
  217. }
  218. impl Inode for FileInode {
  219. fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
  220. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  221. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  222. let mut temp_buf = vec![0u8; buffer.total()];
  223. match ext4fs.inner.read(self.ino as u32, offset, &mut temp_buf) {
  224. Ok(bytes_read) => {
  225. let _ = buffer.fill(&temp_buf[..bytes_read])?;
  226. Ok(buffer.wrote())
  227. }
  228. Err(e) => Err(e.code() as u32),
  229. }
  230. }
  231. fn write(&self, stream: &mut dyn Stream, offset: WriteOffset) -> KResult<usize> {
  232. let _lock = Task::block_on(self.rwsem.write());
  233. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  234. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  235. let mut temp_buf = vec![0u8; 4096];
  236. let mut total_written = 0;
  237. let offset = match offset {
  238. WriteOffset::Position(offset) => offset,
  239. // TODO: here need to add some operate
  240. WriteOffset::End(end) => *end,
  241. };
  242. while let Some(data) = stream.poll_data(&mut temp_buf)? {
  243. let written = ext4fs
  244. .inner
  245. .write(self.ino as u32, offset + total_written, data)
  246. .unwrap();
  247. total_written += written;
  248. if written < data.len() {
  249. break;
  250. }
  251. }
  252. let mtime = Instant::now();
  253. *self.mtime.lock() = mtime;
  254. let new_size = (offset + total_written) as u64;
  255. self.size
  256. .store(offset as u64 + total_written as u64, Ordering::Relaxed);
  257. ext4fs.modify_inode_stat(
  258. self.ino as u32,
  259. Some(new_size),
  260. mtime.since_epoch().as_secs() as u32,
  261. );
  262. Ok(total_written)
  263. }
  264. fn chmod(&self, mode: Mode) -> KResult<()> {
  265. let _lock = Task::block_on(self.rwsem.write());
  266. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  267. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  268. let old_mode = self.mode.load(Ordering::Relaxed);
  269. let new_mode = (old_mode & !0o777) | (mode & 0o777);
  270. let now = Instant::now();
  271. ext4fs.chmod_stat(
  272. self.ino as u32,
  273. new_mode as u16,
  274. now.since_epoch().as_secs() as u32,
  275. );
  276. // SAFETY: `rwsem` has done the synchronization
  277. self.mode.store(new_mode, Ordering::Relaxed);
  278. *self.ctime.lock() = now;
  279. Ok(())
  280. }
  281. // TODO
  282. fn truncate(&self, length: usize) -> KResult<()> {
  283. Ok(())
  284. }
  285. }
  286. impl DirInode {
  287. fn new(ino: Ino, vfs: Weak<dyn Vfs>, mode: Mode) -> Arc<Self> {
  288. Arc::new_cyclic(|_| DirInode {
  289. idata: {
  290. let inode_data = InodeData::new(ino, vfs);
  291. inode_data
  292. .mode
  293. .store(S_IFDIR | (mode & 0o777), Ordering::Relaxed);
  294. inode_data.nlink.store(2, Ordering::Relaxed);
  295. inode_data.size.store(4096, Ordering::Relaxed);
  296. inode_data
  297. },
  298. })
  299. }
  300. fn update_time(&self, time: Instant) {
  301. *self.ctime.lock() = time;
  302. *self.mtime.lock() = time;
  303. }
  304. fn update_child_time(&self, child: &dyn Inode, time: Instant) {
  305. self.update_time(time);
  306. *child.ctime.lock() = time;
  307. *child.mtime.lock() = time;
  308. }
  309. fn link_file(&self) {
  310. self.size.fetch_add(1, Ordering::Relaxed);
  311. }
  312. fn link_dir(&self) {
  313. self.nlink.fetch_add(1, Ordering::Relaxed);
  314. self.size.fetch_add(1, Ordering::Relaxed);
  315. }
  316. fn unlink_dir(&self) {
  317. self.nlink.fetch_sub(1, Ordering::Relaxed);
  318. }
  319. }
  320. impl Inode for DirInode {
  321. fn lookup(&self, dentry: &Arc<Dentry>) -> KResult<Option<Arc<dyn Inode>>> {
  322. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  323. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  324. let name = dentry.get_name();
  325. let name = String::from_utf8_lossy(&name);
  326. let lookup_result = ext4fs.inner.lookup(self.ino as u32, &name);
  327. // TODO: wtf
  328. //const EXT4_ERROR_ENOENT: Ext4Error_ = Ext4Error_::new(ErrCode::ENOENT);
  329. let attr = match lookup_result {
  330. Ok(inode_id) => ext4fs.inner.getattr(inode_id).unwrap(),
  331. //Err(EXT4_ERROR_ENOENT) => return Ok(None),
  332. Err(error) => return Err(error.code() as u32),
  333. };
  334. // Fast path: if the inode is already in the cache, return it.
  335. if let Some(inode) = ext4fs.try_get(&Task::block_on(ext4fs.icache.read()), attr.ino as u64)
  336. {
  337. return Ok(Some(inode));
  338. }
  339. let file_type_bits = match attr.ftype {
  340. FileType::RegularFile => InodeMode::FILE.bits(),
  341. FileType::Directory => InodeMode::DIRECTORY.bits(),
  342. FileType::CharacterDev => InodeMode::CHARDEV.bits(),
  343. FileType::BlockDev => InodeMode::BLOCKDEV.bits(),
  344. FileType::Fifo => InodeMode::FIFO.bits(),
  345. FileType::Socket => InodeMode::SOCKET.bits(),
  346. FileType::SymLink => InodeMode::SOFTLINK.bits(),
  347. FileType::Unknown => 0,
  348. };
  349. let perm_bits = attr.perm.bits() & InodeMode::PERM_MASK.bits();
  350. let mode = file_type_bits | perm_bits;
  351. // Create a new inode based on the attributes.
  352. let mut icache = Task::block_on(ext4fs.icache.write());
  353. let inode = ext4fs.get_or_insert(
  354. &mut icache,
  355. InodeData {
  356. ino: attr.ino as Ino,
  357. size: AtomicU64::new(attr.size),
  358. nlink: AtomicNlink::new(attr.links as _),
  359. uid: AtomicU32::new(attr.uid),
  360. gid: AtomicU32::new(attr.gid),
  361. mode: AtomicU32::new(mode as u32),
  362. atime: Spin::new(Instant::new(attr.atime as _, 0)),
  363. ctime: Spin::new(Instant::new(attr.ctime as _, 0)),
  364. mtime: Spin::new(Instant::new(attr.mtime as _, 0)),
  365. rwsem: RwLock::new(()),
  366. vfs: self.vfs.clone(),
  367. },
  368. );
  369. Ok(Some(inode))
  370. }
  371. fn do_readdir(
  372. &self,
  373. offset: usize,
  374. callback: &mut dyn FnMut(&[u8], Ino) -> KResult<core::ops::ControlFlow<(), ()>>,
  375. ) -> KResult<usize> {
  376. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  377. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  378. let entries = ext4fs
  379. .inner
  380. .listdir(self.ino as u32)
  381. .map_err(|err| err.code() as u32)?;
  382. let entries_to_process = if offset < entries.len() {
  383. &entries[offset..]
  384. } else {
  385. &entries[0..0]
  386. };
  387. let mut current_offset = 0;
  388. for entry in entries_to_process {
  389. let name_string = entry.name();
  390. let name = name_string.as_bytes();
  391. let inode = entry.inode() as Ino;
  392. if callback(name, inode)?.is_break() {
  393. break;
  394. }
  395. current_offset += 1;
  396. }
  397. Ok(current_offset)
  398. }
  399. fn creat(&self, at: &Arc<Dentry>, mode: Mode) -> KResult<()> {
  400. let _lock = Task::block_on(self.rwsem.write());
  401. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  402. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  403. let name = at.get_name();
  404. let name = String::from_utf8_lossy(&name);
  405. let new_ino = ext4fs
  406. .inner
  407. .create(
  408. self.ino as u32,
  409. &name,
  410. InodeMode::from_bits_retain((mode | S_IFREG) as u16),
  411. )
  412. .unwrap();
  413. let file = FileInode::new(new_ino as u64, self.vfs.clone(), mode);
  414. let now = Instant::now();
  415. self.update_child_time(file.as_ref(), now);
  416. self.link_file();
  417. ext4fs.create_inode_stat(self.ino as u32, new_ino, now.since_epoch().as_secs() as u32);
  418. at.save_reg(file)
  419. }
  420. fn mkdir(&self, at: &Dentry, mode: Mode) -> KResult<()> {
  421. let _lock = Task::block_on(self.rwsem.write());
  422. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  423. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  424. let name = at.get_name();
  425. let name = String::from_utf8_lossy(&name);
  426. let new_ino = ext4fs
  427. .inner
  428. .mkdir(
  429. self.ino as u32,
  430. &name,
  431. InodeMode::from_bits_retain((mode | S_IFDIR) as u16),
  432. )
  433. .unwrap();
  434. let new_dir = DirInode::new(new_ino as u64, self.vfs.clone(), mode);
  435. let now = Instant::now();
  436. self.update_child_time(new_dir.as_ref(), now);
  437. self.link_dir();
  438. ext4fs.create_inode_stat(self.ino as u32, new_ino, now.since_epoch().as_secs() as u32);
  439. at.save_dir(new_dir)
  440. }
  441. fn unlink(&self, at: &Arc<Dentry>) -> KResult<()> {
  442. let _dir_lock = Task::block_on(self.rwsem.write());
  443. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  444. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  445. let file = at.get_inode()?;
  446. let name = at.get_name();
  447. let name = String::from_utf8_lossy(&name);
  448. let _file_lock = Task::block_on(file.rwsem.write());
  449. if file.is_dir() {
  450. let _ = ext4fs.inner.rmdir(self.ino as u32, &name);
  451. self.unlink_dir();
  452. } else {
  453. let _ = ext4fs.inner.unlink(self.ino as u32, &name);
  454. }
  455. let now = Instant::now();
  456. self.update_time(now);
  457. ext4fs.modify_inode_stat(self.ino as u32, None, now.since_epoch().as_secs() as u32);
  458. dcache::d_remove(at);
  459. Ok(())
  460. }
  461. fn chmod(&self, mode: Mode) -> KResult<()> {
  462. let _lock = Task::block_on(self.rwsem.write());
  463. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  464. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  465. let old_mode = self.mode.load(Ordering::Relaxed);
  466. let new_mode = (old_mode & !0o777) | (mode & 0o777);
  467. let now = Instant::now();
  468. ext4fs.chmod_stat(
  469. self.ino as u32,
  470. new_mode as u16,
  471. now.since_epoch().as_secs() as u32,
  472. );
  473. // SAFETY: `rwsem` has done the synchronization
  474. self.mode.store(new_mode, Ordering::Relaxed);
  475. *self.ctime.lock() = now;
  476. Ok(())
  477. }
  478. fn rename(&self, rename_data: RenameData) -> KResult<()> {
  479. let RenameData {
  480. old_dentry,
  481. new_dentry,
  482. new_parent,
  483. is_exchange,
  484. no_replace,
  485. vfs,
  486. } = rename_data;
  487. if is_exchange {
  488. println_warn!("Ext4Fs does not support exchange rename for now");
  489. return Err(ENOSYS);
  490. }
  491. // TODO: may need another lock
  492. let _lock = Task::block_on(self.rwsem.write());
  493. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  494. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  495. let old_file = old_dentry.get_inode()?;
  496. let new_file = new_dentry.get_inode();
  497. if no_replace && new_file.is_ok() {
  498. return Err(EEXIST);
  499. }
  500. let name = old_dentry.name();
  501. let name = core::str::from_utf8(&*name).map_err(|_| EINVAL)?;
  502. let new_name = new_dentry.name();
  503. let new_name = core::str::from_utf8(&*new_name).map_err(|_| EINVAL)?;
  504. ext4fs
  505. .inner
  506. .rename(self.ino as u32, name, new_parent.ino as u32, new_name)
  507. .map_err(|err| err.code() as u32)?;
  508. // TODO: may need more operations
  509. let now = Instant::now();
  510. *old_file.ctime.lock() = now;
  511. *self.mtime.lock() = now;
  512. let same_parent = Arc::as_ptr(&new_parent) == &raw const *self;
  513. if !same_parent {
  514. *new_parent.mtime.lock() = now;
  515. if old_file.is_dir() {
  516. self.nlink.fetch_sub(1, Ordering::Relaxed);
  517. new_parent.nlink.fetch_add(1, Ordering::Relaxed);
  518. }
  519. }
  520. if let Ok(replaced_file) = new_dentry.get_inode() {
  521. if !no_replace {
  522. *replaced_file.ctime.lock() = now;
  523. replaced_file.nlink.fetch_sub(1, Ordering::Relaxed);
  524. }
  525. }
  526. Task::block_on(dcache::d_exchange(old_dentry, new_dentry));
  527. Ok(())
  528. }
  529. }
  530. struct Ext4MountCreator;
  531. impl MountCreator for Ext4MountCreator {
  532. fn check_signature(&self, mut first_block: &[u8]) -> KResult<bool> {
  533. match first_block.split_off(1080..) {
  534. Some([0x53, 0xef, ..]) => Ok(true), // Superblock signature
  535. Some(..) => Ok(false),
  536. None => Err(EIO),
  537. }
  538. }
  539. fn create_mount(&self, source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
  540. let source = source.as_bytes();
  541. let path = Path::new(source)?;
  542. let device_dentry =
  543. Dentry::open_recursive(&FsContext::global(), Dentry::root(), path, true, 0)?;
  544. let devid = device_dentry.get_inode()?.devid()?;
  545. let device = BlockDevice::get(devid)?;
  546. let (ext4fs, root_inode) = Ext4Fs::create(device)?;
  547. Mount::new(mp, ext4fs, root_inode)
  548. }
  549. }
  550. pub fn init() {
  551. register_filesystem("ext4", Arc::new(Ext4MountCreator)).unwrap();
  552. }