ext4.rs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. use core::sync::atomic::{AtomicU32, AtomicU64};
  2. use crate::{
  3. io::{Buffer, ByteBuffer},
  4. kernel::{
  5. block::BlockDevice,
  6. constants::EIO,
  7. vfs::{
  8. dentry::Dentry,
  9. inode::{define_struct_inode, AtomicNlink, Ino, Inode, InodeData},
  10. mount::{register_filesystem, Mount, MountCreator},
  11. s_isdir, s_isreg,
  12. vfs::Vfs,
  13. DevId, FsContext, TimeSpec,
  14. },
  15. },
  16. path::Path,
  17. prelude::*,
  18. };
  19. use alloc::{
  20. collections::btree_map::{BTreeMap, Entry},
  21. sync::Arc,
  22. };
  23. use eonix_runtime::task::Task;
  24. use eonix_sync::RwLock;
  25. use ext4_rs::{BlockDevice as Ext4BlockDeviceTrait, Ext4Error};
  26. use ext4_rs::{Errno, Ext4};
  27. pub struct Ext4BlockDevice {
  28. device: Arc<BlockDevice>,
  29. }
  30. impl Ext4BlockDevice {
  31. pub fn new(device: Arc<BlockDevice>) -> Self {
  32. Self { device }
  33. }
  34. }
  35. impl Ext4BlockDeviceTrait for Ext4BlockDevice {
  36. fn read_offset(&self, offset: usize) -> Vec<u8> {
  37. let mut buffer = vec![0u8; 4096];
  38. let mut byte_buffer = ByteBuffer::new(buffer.as_mut_slice());
  39. let _ = self
  40. .device
  41. .read_some(offset, &mut byte_buffer)
  42. .expect("Failed to read from block device");
  43. buffer
  44. }
  45. fn write_offset(&self, _offset: usize, _data: &[u8]) {
  46. todo!()
  47. }
  48. }
  49. impl_any!(Ext4Fs);
  50. struct Ext4Fs {
  51. inner: Ext4,
  52. device: Arc<BlockDevice>,
  53. icache: RwLock<BTreeMap<Ino, Ext4Inode>>,
  54. }
  55. impl Vfs for Ext4Fs {
  56. fn io_blksize(&self) -> usize {
  57. 4096
  58. }
  59. fn fs_devid(&self) -> DevId {
  60. self.device.devid()
  61. }
  62. fn is_read_only(&self) -> bool {
  63. true
  64. }
  65. }
  66. impl Ext4Fs {
  67. fn try_get(&self, icache: &BTreeMap<Ino, Ext4Inode>, ino: u64) -> Option<Arc<dyn Inode>> {
  68. icache.get(&ino).cloned().map(Ext4Inode::into_inner)
  69. }
  70. fn get_or_insert(
  71. &self,
  72. icache: &mut BTreeMap<Ino, Ext4Inode>,
  73. mut idata: InodeData,
  74. ) -> Arc<dyn Inode> {
  75. match icache.entry(idata.ino) {
  76. Entry::Occupied(occupied) => occupied.get().clone().into_inner(),
  77. Entry::Vacant(vacant) => {
  78. let mode = *idata.mode.get_mut();
  79. if s_isreg(mode) {
  80. vacant
  81. .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
  82. .clone()
  83. .into_inner()
  84. } else if s_isdir(mode) {
  85. vacant
  86. .insert(Ext4Inode::Dir(Arc::new(DirInode { idata })))
  87. .clone()
  88. .into_inner()
  89. } else {
  90. println_warn!("ext4: Unsupported inode type: {mode:#o}");
  91. vacant
  92. .insert(Ext4Inode::File(Arc::new(FileInode { idata })))
  93. .clone()
  94. .into_inner()
  95. }
  96. }
  97. }
  98. }
  99. }
  100. impl Ext4Fs {
  101. pub fn create(device: Arc<BlockDevice>) -> KResult<(Arc<Self>, Arc<dyn Inode>)> {
  102. let ext4_device = Ext4BlockDevice::new(device.clone());
  103. let ext4 = Ext4::open(Arc::new(ext4_device));
  104. let ext4fs = Arc::new(Self {
  105. inner: ext4,
  106. device,
  107. icache: RwLock::new(BTreeMap::new()),
  108. });
  109. let root_inode = {
  110. let mut icache = Task::block_on(ext4fs.icache.write());
  111. let root_inode = ext4fs.inner.get_inode_ref(2);
  112. ext4fs.get_or_insert(
  113. &mut icache,
  114. InodeData {
  115. ino: root_inode.inode_num as Ino,
  116. size: AtomicU64::new(root_inode.inode.size()),
  117. nlink: AtomicNlink::new(root_inode.inode.links_count() as _),
  118. uid: AtomicU32::new(root_inode.inode.uid() as _),
  119. gid: AtomicU32::new(root_inode.inode.gid() as _),
  120. mode: AtomicU32::new(root_inode.inode.mode() as _),
  121. atime: Spin::new(TimeSpec {
  122. sec: root_inode.inode.atime() as _,
  123. nsec: root_inode.inode.i_atime_extra() as _,
  124. }),
  125. ctime: Spin::new(TimeSpec {
  126. sec: root_inode.inode.ctime() as _,
  127. nsec: root_inode.inode.i_ctime_extra() as _,
  128. }),
  129. mtime: Spin::new(TimeSpec {
  130. sec: root_inode.inode.mtime() as _,
  131. nsec: root_inode.inode.i_mtime_extra() as _,
  132. }),
  133. rwsem: RwLock::new(()),
  134. vfs: Arc::downgrade(&ext4fs) as _,
  135. },
  136. )
  137. };
  138. Ok((ext4fs, root_inode))
  139. }
  140. }
  141. #[derive(Clone)]
  142. enum Ext4Inode {
  143. File(Arc<FileInode>),
  144. Dir(Arc<DirInode>),
  145. }
  146. impl Ext4Inode {
  147. fn into_inner(self) -> Arc<dyn Inode> {
  148. match self {
  149. Ext4Inode::File(inode) => inode,
  150. Ext4Inode::Dir(inode) => inode,
  151. }
  152. }
  153. }
  154. define_struct_inode! {
  155. struct FileInode;
  156. }
  157. define_struct_inode! {
  158. struct DirInode;
  159. }
  160. impl Inode for FileInode {
  161. fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
  162. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  163. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  164. let mut temp_buf = vec![0u8; buffer.total()];
  165. match ext4fs.inner.read_at(self.ino as u32, offset, &mut temp_buf) {
  166. Ok(bytes_read) => {
  167. let _ = buffer.fill(&temp_buf[..bytes_read])?;
  168. Ok(buffer.wrote())
  169. }
  170. Err(e) => Err(e.error() as u32),
  171. }
  172. }
  173. }
  174. impl Inode for DirInode {
  175. fn lookup(&self, dentry: &Arc<Dentry>) -> KResult<Option<Arc<dyn Inode>>> {
  176. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  177. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  178. let name = String::from_utf8_lossy(&dentry.name());
  179. let lookup_result = ext4fs.inner.fuse_lookup(self.ino, &name);
  180. const EXT4_ERROR_ENOENT: Ext4Error = Ext4Error::new(Errno::ENOENT);
  181. let attr = match lookup_result {
  182. Ok(attr) => attr,
  183. Err(EXT4_ERROR_ENOENT) => return Ok(None),
  184. Err(error) => return Err(error.error() as u32),
  185. };
  186. // Fast path: if the inode is already in the cache, return it.
  187. if let Some(inode) = ext4fs.try_get(&Task::block_on(ext4fs.icache.read()), attr.ino as u64)
  188. {
  189. return Ok(Some(inode));
  190. }
  191. let extra_perm = attr.perm.bits() as u32 & 0o7000;
  192. let perm = attr.perm.bits() as u32 & 0o0700;
  193. let real_perm = extra_perm | perm | perm >> 3 | perm >> 6;
  194. // Create a new inode based on the attributes.
  195. let mut icache = Task::block_on(ext4fs.icache.write());
  196. let inode = ext4fs.get_or_insert(
  197. &mut icache,
  198. InodeData {
  199. ino: attr.ino as Ino,
  200. size: AtomicU64::new(attr.size),
  201. nlink: AtomicNlink::new(attr.nlink as _),
  202. uid: AtomicU32::new(attr.uid),
  203. gid: AtomicU32::new(attr.gid),
  204. mode: AtomicU32::new(attr.kind.bits() as u32 | real_perm),
  205. atime: Spin::new(TimeSpec {
  206. sec: attr.atime as _,
  207. nsec: 0,
  208. }),
  209. ctime: Spin::new(TimeSpec {
  210. sec: attr.ctime as _,
  211. nsec: 0,
  212. }),
  213. mtime: Spin::new(TimeSpec {
  214. sec: attr.mtime as _,
  215. nsec: 0,
  216. }),
  217. rwsem: RwLock::new(()),
  218. vfs: self.vfs.clone(),
  219. },
  220. );
  221. Ok(Some(inode))
  222. }
  223. fn do_readdir(
  224. &self,
  225. offset: usize,
  226. callback: &mut dyn FnMut(&[u8], Ino) -> KResult<core::ops::ControlFlow<(), ()>>,
  227. ) -> KResult<usize> {
  228. let vfs = self.vfs.upgrade().ok_or(EIO)?;
  229. let ext4fs = vfs.as_any().downcast_ref::<Ext4Fs>().unwrap();
  230. let entries = ext4fs
  231. .inner
  232. .fuse_readdir(self.ino as u64, 0, offset as i64)
  233. .map_err(|err| err.error() as u32)?;
  234. let mut current_offset = 0;
  235. for entry in entries {
  236. let name_len = entry.name_len as usize;
  237. let name = &entry.name[..name_len];
  238. if callback(name, entry.inode as Ino)?.is_break() {
  239. break;
  240. }
  241. current_offset += 1;
  242. }
  243. Ok(current_offset)
  244. }
  245. }
  246. struct Ext4MountCreator;
  247. impl MountCreator for Ext4MountCreator {
  248. fn check_signature(&self, mut first_block: &[u8]) -> KResult<bool> {
  249. match first_block.split_off(1080..) {
  250. Some([0x53, 0xef, ..]) => Ok(true), // Superblock signature
  251. Some(..) => Ok(false),
  252. None => Err(EIO),
  253. }
  254. }
  255. fn create_mount(&self, source: &str, _flags: u64, mp: &Arc<Dentry>) -> KResult<Mount> {
  256. let source = source.as_bytes();
  257. let path = Path::new(source)?;
  258. let device_dentry =
  259. Dentry::open_recursive(&FsContext::global(), Dentry::root(), path, true, 0)?;
  260. let devid = device_dentry.get_inode()?.devid()?;
  261. let device = BlockDevice::get(devid)?;
  262. let (ext4fs, root_inode) = Ext4Fs::create(device)?;
  263. Mount::new(mp, ext4fs, root_inode)
  264. }
  265. }
  266. pub fn init() {
  267. register_filesystem("ext4", Arc::new(Ext4MountCreator)).unwrap();
  268. }