diff --git a/api/axfeat/Cargo.toml b/api/axfeat/Cargo.toml index b59d1dc3a..6326e108c 100644 --- a/api/axfeat/Cargo.toml +++ b/api/axfeat/Cargo.toml @@ -39,7 +39,8 @@ sched_rr = ["axtask/sched_rr", "irq"] sched_cfs = ["axtask/sched_cfs", "irq"] # File system -fs = ["alloc", "paging", "axdriver/virtio-blk", "dep:axfs", "axruntime/fs"] # TODO: try to remove "paging" +fs = ["alloc", "paging", "dep:axfs", "axruntime/fs"] # TODO: try to remove "paging" +blkfs = ["axdriver/virtio-blk", "axruntime/blkfs"] myfs = ["axfs?/myfs"] 9pfs = [] diff --git a/apps/c/filetest/features.txt b/apps/c/filetest/features.txt index 8457701d1..d42d543b2 100644 --- a/apps/c/filetest/features.txt +++ b/apps/c/filetest/features.txt @@ -1,3 +1,4 @@ alloc paging fs +blkfs diff --git a/apps/c/iperf/features.txt b/apps/c/iperf/features.txt index bf42f8cfe..176c573d0 100644 --- a/apps/c/iperf/features.txt +++ b/apps/c/iperf/features.txt @@ -2,5 +2,6 @@ alloc paging net fs +blkfs select fp_simd diff --git a/apps/c/redis/features.txt b/apps/c/redis/features.txt index 380d6e395..65a95f0e3 100644 --- a/apps/c/redis/features.txt +++ b/apps/c/redis/features.txt @@ -4,6 +4,7 @@ fp_simd irq multitask fs +blkfs net pipe epoll diff --git a/apps/c/sqlite3/features.txt b/apps/c/sqlite3/features.txt index e1814d00a..488f57a29 100644 --- a/apps/c/sqlite3/features.txt +++ b/apps/c/sqlite3/features.txt @@ -2,3 +2,4 @@ fp_simd alloc paging fs +blkfs diff --git a/apps/fs/shell/Cargo.toml b/apps/fs/shell/Cargo.toml index 887af2aa2..25d70a3af 100644 --- a/apps/fs/shell/Cargo.toml +++ b/apps/fs/shell/Cargo.toml @@ -14,4 +14,4 @@ default = [] axfs_vfs = { path = "../../../crates/axfs_vfs", optional = true } axfs_ramfs = { path = "../../../crates/axfs_ramfs", optional = true } crate_interface = { path = "../../../crates/crate_interface", optional = true } -axstd = { path = "../../../ulib/axstd", features = ["alloc", "fs", "virtio-9p"], optional = true } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "fs","blkfs"], optional = true } diff --git a/modules/ax9p/src/drv.rs b/modules/ax9p/src/drv.rs index e2ae6f7f4..7330e602b 100644 --- a/modules/ax9p/src/drv.rs +++ b/modules/ax9p/src/drv.rs @@ -14,10 +14,11 @@ use axdriver::prelude::*; use log::*; use spin::RwLock; -const UNDEFINED_ERROR: u8 = 0; +const EIO: u8 = 5; +const EINVAL: u8 = 90; const _9P_LEAST_QLEN: u32 = 7; // size[4] type_id[1] tag[2] -const _9P_MAX_QSIZE: u32 = 8192 + 1; // it should larger than 8192, or virtio-9p may raise a warning. -const _9P_MAX_PSIZE: u32 = 8192 + 1; // it should larger than 8192, or virtio-9p may raise a warning. +pub const _9P_MAX_QSIZE: u32 = 8192 + 1; // it should larger than 8192, or virtio-9p may raise a warning. +pub const _9P_MAX_PSIZE: u32 = 8192 + 1; // it should larger than 8192, or virtio-9p may raise a warning. const _9P_NONUNAME: u32 = 0; pub const _9P_SETATTR_MODE: u64 = 0x00000001; @@ -55,6 +56,9 @@ impl Drv9pOps { // Send request and receive response pub fn request(&mut self, request: &[u8], response: &mut [u8]) -> Result<(), u8> { + if request.len() as u32 > _9P_MAX_QSIZE { + return Err(EINVAL); + } let enqueue_try = self.transport.write().send_with_recv(request, response); match enqueue_try { Ok(_) => { @@ -72,7 +76,7 @@ impl Drv9pOps { _ => Ok(()), } } - Err(_) => Err(UNDEFINED_ERROR), + Err(_) => Err(EIO), } } @@ -166,6 +170,7 @@ impl Drv9pOps { self.request(&request.buffer, &mut response_buffer) } + /// `twalk()`: Pay attention to the max_size of request buffer, wnames should not be too long usually. pub fn twalk(&mut self, fid: u32, newfid: u32, nwname: u16, wnames: &[&str]) -> Result<(), u8> { let mut request = _9PReq::new(_9PType::Twalk); let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; @@ -282,12 +287,19 @@ impl Drv9pOps { /// read perform I/O on the file represented by fid. /// Note that in v9fs, a read(2) or write(2) system call for a chunk of the file that won't fit in a single request is broken up into multiple requests. + /// `tread()` can only read _9P_MAX_PSIZE-25 bytes if counter is larger than _9P_MAX_PSIZE. pub fn tread(&mut self, fid: u32, offset: u64, count: u32) -> Result, u8> { + // check if `count` larger than MAX_READ_LEN + const MAX_READ_LEN: u32 = _9P_MAX_PSIZE - 32; + let mut reading_len = count; + if reading_len > MAX_READ_LEN { + reading_len = MAX_READ_LEN; + } let mut request = _9PReq::new(_9PType::Tread); let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; request.write_u32(fid); request.write_u64(offset); - request.write_u32(count); + request.write_u32(reading_len); request.finish(); match self.request(&request.buffer, &mut response_buffer) { Ok(_) => { @@ -300,54 +312,113 @@ impl Drv9pOps { } } - /// read directory represented by fid in 9P2000.u. In 9P2000.L, using treaddir() instead. - pub fn u_treaddir(&mut self, fid: u32, offset: u64, count: u32) -> Result, u8> { - let mut request = _9PReq::new(_9PType::Tread); - let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; - request.write_u32(fid); - request.write_u64(offset); - request.write_u32(count); - request.finish(); - match self.request(&request.buffer, &mut response_buffer) { - Ok(_) => { - const COUNT_START: usize = 7; - const COUNT_END: usize = 11; - let length: u64 = lbytes2u64(&response_buffer[COUNT_START..COUNT_START + 4]); - - let mut dir_entries: Vec = Vec::new(); - let mut resp_ptr = COUNT_END; - while resp_ptr < length as usize { - // qid[13] offset[8] type[1] name[s] - let state = UStatFs::parse_u_from(&response_buffer[resp_ptr..]); - let dir_entry = DirEntry { - qid: state.get_qid(), - offset: resp_ptr as u64, - dtype: state.get_ftype(), - name: state.get_name(), - }; - resp_ptr += state.get_self_length(); - dir_entries.push(dir_entry); + pub fn treaddir(&mut self, fid: u32) -> Result, u8> { + let mut dir_entries: Vec = Vec::new(); + let mut offptr = 0_u64; + loop { + // check if `count` larger than MAX_READ_LEN + const MAX_READ_LEN: u32 = _9P_MAX_PSIZE - 32; + + let mut request = _9PReq::new(_9PType::Treaddir); + let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; + request.write_u32(fid); + request.write_u64(offptr); + request.write_u32(MAX_READ_LEN); + request.finish(); + match self.request(&request.buffer, &mut response_buffer) { + Ok(_) => { + const COUNT_START: usize = 7; + const COUNT_END: usize = 11; + let length: u64 = lbytes2u64(&response_buffer[COUNT_START..COUNT_START + 4]); + if length == 0 { + break; + } + + let mut resp_ptr = COUNT_END; + while resp_ptr < length as usize { + // qid[13] offset[8] type[1] name[s] + let dir_entry = DirEntry { + qid: _9PQid::new(&response_buffer[resp_ptr..resp_ptr + 13]), + offset: lbytes2u64(&response_buffer[resp_ptr + 13..resp_ptr + 21]), + dtype: response_buffer[resp_ptr + 21], + name: lbytes2str(&response_buffer[resp_ptr + 22..]), + }; + resp_ptr += 24 + dir_entry.name.len(); + offptr = dir_entry.offset; + dir_entries.push(dir_entry); + } } + Err(err_code) => return Err(err_code), + } + } + Ok(dir_entries) + } - Ok(dir_entries) + /// read directory represented by fid in 9P2000.u. In 9P2000.L, using treaddir() instead. + pub fn u_treaddir(&mut self, fid: u32) -> Result, u8> { + let mut dir_entries: Vec = Vec::new(); + let mut offptr = 0_u64; + loop { + // check if `count` larger than MAX_READ_LEN + const MAX_READ_LEN: u32 = _9P_MAX_PSIZE - 32; + + let mut request = _9PReq::new(_9PType::Tread); + let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; + request.write_u32(fid); + request.write_u64(offptr); + request.write_u32(MAX_READ_LEN); + request.finish(); + match self.request(&request.buffer, &mut response_buffer) { + Ok(_) => { + const COUNT_START: usize = 7; + const COUNT_END: usize = 11; + let length: u64 = lbytes2u64(&response_buffer[COUNT_START..COUNT_START + 4]); + if length == 0 { + break; + } + + let mut resp_ptr = COUNT_END; + while resp_ptr < length as usize { + // qid[13] offset[8] type[1] name[s] + let state = UStatFs::parse_u_from(&response_buffer[resp_ptr..]); + let dir_entry = DirEntry { + qid: state.get_qid(), + offset: resp_ptr as u64, + dtype: state.get_ftype(), + name: state.get_name(), + }; + resp_ptr += state.get_self_length(); + offptr = dir_entry.offset; + dir_entries.push(dir_entry); + } + } + Err(err_code) => return Err(err_code), } - Err(err_code) => Err(err_code), } + Ok(dir_entries) } /// write perform I/O on the file represented by fid. /// Note that in v9fs, a read(2) or write(2) system call for a chunk of the file that won't fit in a single request is broken up into multiple requests. - pub fn twrite(&mut self, fid: u32, offset: u64, data: &[u8]) -> Result<(), u8> { + pub fn twrite(&mut self, fid: u32, offset: u64, data: &[u8]) -> Result { + const MAX_READ_LEN: u32 = _9P_MAX_PSIZE - 32; + let mut writing_len = data.len() as u32; + if writing_len > MAX_READ_LEN { + writing_len = MAX_READ_LEN; + } let mut request = _9PReq::new(_9PType::Twrite); let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; request.write_u32(fid); request.write_u64(offset); - request.write_u32(data.len() as u32); - for value in data { + request.write_u32(writing_len); + for value in &data[..writing_len as usize] { request.write_u8(*value); } request.finish(); - self.request(&request.buffer, &mut response_buffer) + match self.request(&request.buffer, &mut response_buffer) { + Ok(_) => Ok(lbytes2u64(&response_buffer[7..11]) as u8), // index from 7 to 11 corresponing to total count of writed byte + Err(ecode) => Err(ecode), + } } pub fn tmkdir(&mut self, dfid: u32, name: &str, mode: u32, gid: u32) -> Result<(), u8> { @@ -361,39 +432,6 @@ impl Drv9pOps { self.request(&request.buffer, &mut response_buffer) } - pub fn treaddir(&mut self, fid: u32, offset: u64, count: u32) -> Result, u8> { - let mut request = _9PReq::new(_9PType::Treaddir); - let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; - request.write_u32(fid); - request.write_u64(offset); - request.write_u32(count); - request.finish(); - match self.request(&request.buffer, &mut response_buffer) { - Ok(_) => { - const COUNT_START: usize = 7; - const COUNT_END: usize = 11; - let length: u64 = lbytes2u64(&response_buffer[COUNT_START..COUNT_START + 4]); - - let mut dir_entries: Vec = Vec::new(); - let mut resp_ptr = COUNT_END; - while resp_ptr < length as usize { - // qid[13] offset[8] type[1] name[s] - let dir_entry = DirEntry { - qid: _9PQid::new(&response_buffer[resp_ptr..resp_ptr + 13]), - offset: lbytes2u64(&response_buffer[resp_ptr + 13..resp_ptr + 21]), - dtype: response_buffer[resp_ptr + 21], - name: lbytes2str(&response_buffer[resp_ptr + 22..]), - }; - resp_ptr += 24 + dir_entry.name.len(); - dir_entries.push(dir_entry); - } - - Ok(dir_entries) - } - Err(err_code) => Err(err_code), - } - } - pub fn tremove(&mut self, fid: u32) -> Result<(), u8> { let mut request = _9PReq::new(_9PType::Tremove); let mut response_buffer: [u8; _9P_MAX_PSIZE as usize] = [0; _9P_MAX_PSIZE as usize]; diff --git a/modules/ax9p/src/fs.rs b/modules/ax9p/src/fs.rs index d693c5ec8..3dd5da9b3 100644 --- a/modules/ax9p/src/fs.rs +++ b/modules/ax9p/src/fs.rs @@ -111,7 +111,6 @@ pub struct DirNode { fid: Arc, protocol: Arc, parent: RwLock>, - children: RwLock>, } impl DirNode { @@ -145,7 +144,6 @@ impl DirNode { fid: Arc::new(fid), protocol, parent: RwLock::new(parent.unwrap_or_else(|| Weak::::new())), - children: RwLock::new(BTreeMap::new()), }) } @@ -245,19 +243,16 @@ impl DirNode { /// Update nodes from host filesystem fn read_nodes(&self) -> Result, VfsError> { let mut node_map: BTreeMap = BTreeMap::new(); - // TODO: the length of dir content can only support as large as MAX_LENGTH. - const OFFSET: u64 = 0; - const MAX_LENGTH: u32 = 1024; debug!("reading nodes"); let dirents = match self.protocol.as_str() { - "9P2000.L" => match self.inner.write().treaddir(*self.fid, OFFSET, MAX_LENGTH) { + "9P2000.L" => match self.inner.write().treaddir(*self.fid) { Ok(contents) => contents, Err(errcode) => { error!("9pfs treaddir failed! error code: {}", errcode); return Err(VfsError::BadState); } }, - "9P2000.u" => match self.inner.write().u_treaddir(*self.fid, OFFSET, MAX_LENGTH) { + "9P2000.u" => match self.inner.write().u_treaddir(*self.fid) { Ok(contents) => contents, Err(errcode) => { error!("9pfs u_treaddir failed! error code: {}", errcode); @@ -378,12 +373,7 @@ impl VfsNodeOps for DirNode { match name { "" | "." => Ok(self.clone() as VfsNodeRef), ".." => self.parent().ok_or(VfsError::NotFound), - _ => self - .children - .read() - .get(name) - .cloned() - .ok_or(VfsError::NotFound), + _ => Err(VfsError::NotFound), }? } }; @@ -404,21 +394,14 @@ impl VfsNodeOps for DirNode { return Err(VfsError::BadState); } }; - let children: spin::RwLockReadGuard<'_, BTreeMap>> = - self.children.read(); - - // merge mounted tree with 9p's node tree. - let mut children = children - .iter() - .chain(_9p_map.iter()) - .skip(start_idx.max(2) - 2); + let mut item_iter = _9p_map.iter().skip(start_idx.max(2) - 2); for (i, ent) in dirents.iter_mut().enumerate() { match i + start_idx { 0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir), 1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir), _ => { - if let Some((name, node)) = children.next() { + if let Some((name, node)) = item_iter.next() { let attr = node.get_attr(); let file_type = match attr { Ok(attr) => attr.file_type(), @@ -446,8 +429,7 @@ impl VfsNodeOps for DirNode { ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty), _ => { let subdir = self - .children - .read() + .read_nodes()? .get(name) .ok_or(VfsError::NotFound)? .clone(); @@ -594,25 +576,45 @@ impl VfsNodeOps for FileNode { /// Read data from the file at the given offset. fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { let mut dev = self.inner.write(); - match dev.tread(*self.fid, offset, buf.len() as u32) { - Ok(content) => { - // should be more effient - for (i, byte) in content.iter().enumerate() { - buf[i] = *byte; + let mut read_len = buf.len(); + let mut offset_ptr = 0; + while read_len > 0 { + let target_buf = &mut buf[offset_ptr..]; + let rlen = match dev.tread(*self.fid, offset + offset_ptr as u64, read_len as u32) { + Ok(content) => { + let read_len = content.len(); + target_buf[..read_len].copy_from_slice(&content); + read_len } - Ok(content.len()) + Err(_) => return Err(VfsError::BadState), + }; + if rlen == 0 { + return Ok(offset_ptr); } - Err(_) => Err(VfsError::BadState), + read_len -= rlen; + offset_ptr += rlen; } + Ok(buf.len()) } /// Write data to the file at the given offset. fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { let mut dev = self.inner.write(); - match dev.twrite(*self.fid, offset, buf) { - Ok(_) => Ok(buf.len()), - Err(_) => Err(VfsError::BadState), + let mut write_len = buf.len(); + let mut offset_ptr = 0; + while write_len > 0 { + let target_buf = &buf[offset_ptr..]; + let wlen = match dev.twrite(*self.fid, offset + offset_ptr as u64, target_buf) { + Ok(writed_length) => writed_length as usize, + Err(_) => return Err(VfsError::BadState), + }; + if wlen == 0 { + return Ok(offset_ptr); + } + write_len -= wlen; + offset_ptr += wlen; } + Ok(buf.len()) } /// Flush the file, synchronize the data to disk. diff --git a/modules/axfs/src/lib.rs b/modules/axfs/src/lib.rs index 469bedcd5..c1e767bcf 100644 --- a/modules/axfs/src/lib.rs +++ b/modules/axfs/src/lib.rs @@ -57,6 +57,12 @@ cfg_if::cfg_if! { pub use root::MountPoint; +/// Initialize an empty filesystems by ramfs. +#[cfg(not(any(feature = "blkfs", feature = "virtio-9p", feature = "net-9p")))] +pub fn init_tempfs() -> MountPoint { + MountPoint::new("/", mounts::ramfs()) +} + /// Initializes filesystems by block devices. pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint { info!("Initialize filesystems..."); diff --git a/modules/axfs/src/root.rs b/modules/axfs/src/root.rs index 3bdaaaf48..b5ac09c7f 100644 --- a/modules/axfs/src/root.rs +++ b/modules/axfs/src/root.rs @@ -13,7 +13,7 @@ use alloc::{format, string::String, sync::Arc, vec::Vec}; use axerrno::{ax_err, AxError, AxResult}; -use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; +use axfs_vfs::{VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; use axsync::Mutex; use lazy_init::LazyInit; @@ -67,7 +67,14 @@ impl RootDirectory { return ax_err!(InvalidInput, "mount point already exists"); } // create the mount point in the main filesystem if it does not exist - self.main_fs.root_dir().create(path, FileType::Dir)?; + match self.main_fs.root_dir().lookup(path) { + Ok(_) => {} + Err(err_code) => { + if err_code == VfsError::NotFound { + self.main_fs.root_dir().create(path, FileType::Dir)?; + } + } + } fs.mount(path, self.main_fs.root_dir().lookup(path)?)?; self.mounts.push(MountPoint::new(path, fs)); Ok(()) diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index f5d1390c1..6487fbb25 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -21,6 +21,7 @@ rtc = ["axhal/rtc"] multitask = ["axtask/multitask"] fs = ["axdriver", "axfs"] +blkfs = ["fs"] virtio-9p = ["fs", "ax9p"] net-9p = ["fs", "ax9p"] net = ["axdriver", "axnet"] diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index 47dfe0aad..53cc14eb8 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -193,9 +193,13 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { // By default, mount_points[0] will be rootfs let mut mount_points: Vec = Vec::new(); + //setup ramfs as rootfs if no other filesystem can be mounted + #[cfg(not(any(feature = "blkfs", feature = "virtio-9p", feature = "net-9p")))] + mount_points.push(axfs::init_tempfs()); + // setup and initialize blkfs as one mountpoint for rootfs + #[cfg(feature = "blkfs")] mount_points.push(axfs::init_blkfs(all_devices.block)); - axfs::prepare_commonfs(&mut mount_points); // setup and initialize 9pfs as mountpoint #[cfg(feature = "virtio-9p")] @@ -210,6 +214,7 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { option_env!("AX_ANAME_9P").unwrap_or(""), option_env!("AX_PROTOCOL_9P").unwrap_or(""), )); + axfs::prepare_commonfs(&mut mount_points); // setup and initialize rootfs axfs::init_filesystems(mount_points); diff --git a/ulib/axstd/Cargo.toml b/ulib/axstd/Cargo.toml index e04370582..d1a4498cf 100644 --- a/ulib/axstd/Cargo.toml +++ b/ulib/axstd/Cargo.toml @@ -48,6 +48,7 @@ sched_cfs = ["axfeat/sched_cfs"] # File system fs = ["arceos_api/fs", "axfeat/fs"] myfs = ["arceos_api/myfs", "axfeat/myfs"] +blkfs = ["axfeat/blkfs"] virtio-9p = ["axfeat/virtio-9p"] net-9p = ["axfeat/net-9p"]