1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
//! Device driver. //! //! The design should be applicable for such use cases: //! - Env server //! - Tempdir and tempfile: env, //! - Two-way pipe: stdin/stdout, network //! - Event listener on file: Epoll //! - how to retrieve directory that posesses this file and then call its wstat. //! - Donot support atomic renaming due to portable reason, use special ctrl file instead. //! - Donot support link/unlink, use bind and mount instead. Since they provide more abstraction //! and behave more generally and more user-friendly. //! - Donot deal with access control, just r/w permission information. //! //! ## Permission in file system? //! //! Use namespace instead? #![allow(missing_docs)] use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; use bitflags::bitflags; use core::ops::Deref; use crate::{ chan::{Chan, ChanId, ChanKey, ChanKind, Dirent, Perm}, error::{Error, Result}, }; /// Common trait of a device driver. /// /// # Why not use GAT to get async trait? /// /// Since that may cause loop when our FS is dependent on a Chan, and the compiler cannot /// determine the future size at compile time. Think of the case when we have FS on a file, /// which resides on another FS, and the length of such chain cannot be known at compile time. #[allow(missing_docs)] #[async_trait::async_trait_try] pub trait Device { /// The async destructor of the device. /// /// This may be called when some device in the global device table Vec<Arc<Dev>> has /// no reference(i.e. no opened file). async fn shutdown(self) where Self: Sized; /// Return the root channel. `aname` is a user-specified hint. async fn attach(&self, aname: &[u8]) -> Result<ChanId>; /// Open a file in the directory or reopen a file. /// /// If `name` is empty, ignore other parameters and reopen the file itself(same as dup). /// The reopen operation returns either an error or some chan id(but not [None]). /// /// `dir` is guaranteed to be a directory if `name` is not empty. /// /// If `create_dir` is [None], this operation is a normal open. Return [None] if not found. /// /// Otherwise, it means creating a file. The boolean indicates type of the file to be /// created is a directory or not. Return [None] if the file already exist. async fn open( &self, dir: &ChanId, name: &[u8], create_dir: Option<bool>, ) -> Result<Option<ChanId>>; /// Close a file. The drop of channel is done by [Chan::close](`super::chan::Chan::close`) /// /// This function must always succeed. async fn close(&self, c: ChanId); /// Remove a file by hint such that the server won't remove the file until /// all clients have closed it. Can only remove normal file or empty directory. /// May not atomic since having to truncate the file first. /// /// Return true if removed, else false. async fn remove(&self, c: &ChanId) -> Result<bool>; /// Inquires about the file attribute identified by a channel. async fn stat(&self, c: &ChanId) -> Result<Dirent>; /// Change the file attribute identified by a channel. Should be atomic. async fn wstat(&self, c: &ChanId, dirent: &Dirent) -> Result<()>; /// Read data from a channel by offset and store to buffer. /// /// For plain files, it returns the number of bytes read. /// /// For directories, read returns an integral number of directory entries exactly /// as in stat, one for each member of the directory. /// The offset and buffer len must be zero modulo DIRLEN. async fn read(&self, c: &ChanId, buf: &mut [u8], off: usize) -> Result<usize>; /// Write the data in buffer to a channel starting by specific offset. /// Directories are not allowed to be written. /// /// Return the number of bytes writed. async fn write(&self, c: &ChanId, buf: &[u8], off: usize) -> Result<usize>; /// Reduce the size of a file. /// /// If the file previously was larger than or equal to this size, the extra data is lost. /// Otherwise, do nothing. /// /// Return the new size, which may or may not be equal to the expected size. /// /// NOTE: truncate with infinite size can be used to retrieve the size of the file. /// Why not resize? Since resize by write can better handle initial bytes. async fn truncate(&self, c: &ChanId, size: usize) -> Result<usize>; } #[cfg(test)] mod tests { use super::*; use ksched::task; struct A<T>(T); #[async_trait::async_trait_try] impl<T: Send + 'static + Sync> Device for A<T> { async fn shutdown(self) where Self: Sized, { todo!() } async fn attach(&self, aname: &[u8]) -> Result<ChanId> where Self: Sized, { todo!() } async fn open( &self, dir: &ChanId, name: &[u8], create_dir: Option<bool>, ) -> Result<Option<ChanId>> { todo!() } async fn close(&self, c: ChanId) {} async fn remove(&self, c: &ChanId) -> Result<bool> { todo!() } async fn truncate(&self, c: &ChanId, size: usize) -> Result<usize> { todo!(); } async fn stat(&self, c: &ChanId) -> Result<Dirent> { todo!() } async fn wstat(&self, c: &ChanId, dirent: &Dirent) -> Result<()> { todo!() } async fn read(&self, c: &ChanId, buf: &mut [u8], off: usize) -> Result<usize> { println!("c {:?}", c); buf[0] = 10; Ok(0) } async fn write(&self, c: &ChanId, buf: &[u8], off: usize) -> Result<usize> { todo!() } } #[test] fn it_compiles() { // task::spawn(0, async move { // let a = Arc::new(Dev::FAT(A(1u8))); // let key = ChanKey::new( // a.clone(), // 0, // Qid { // path: 0, // version: 0, // qtype: QType::File, // }, // ); // let mut buf = [0u8; 10]; // // Test NewDevice. // let newa = A(1u8); // newa.read(&newa, &key, &mut buf, 0).await.unwrap(); // let file = Arc::new(Chan { // key, // name: Vec::new(), // parent: Weak::new(), // inner: Mutex::new(ChanInner::default()), // }); // file.read(&mut buf, 0).await.unwrap(); // assert_eq!(buf[0], 10); // file.close().await; // }) // .unwrap(); // task::run_all(); } }