From d1e099f5842a0ca27ef75afc3054a024d4dd1e2d Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 10 Oct 2024 16:17:32 +0800 Subject: [PATCH 01/36] fix: getdents --- api/ruxos_posix_api/src/imp/fs.rs | 78 ++++++++++++++++++++----------- crates/axfs_vfs/Cargo.toml | 2 +- modules/ruxfs/Cargo.toml | 1 - modules/ruxfs/src/fops.rs | 10 ++++ 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 89019d865..74ad04b5a 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -120,7 +120,7 @@ impl Directory { let f = super::fd_ops::get_file_like(fd)?; f.into_any() .downcast::() - .map_err(|_| LinuxError::EINVAL) + .map_err(|_| LinuxError::ENOTDIR) } } @@ -216,7 +216,7 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { let path = char_ptr_to_absolute_path(path); let fd: c_int = fd as c_int; - debug!("sys_openat <= {}, {:?}, {:#o} {:#o}", fd, path, flags, mode); + debug!("sys_openat <= {}, {:?}, {:#o}, {:#o}", fd, path, flags, mode); syscall_body!(sys_openat, { let options = flags_to_options(flags, mode); if (flags as u32) & ctypes::O_DIRECTORY != 0 { @@ -595,6 +595,8 @@ pub fn sys_readlinkat( } type LinuxDirent64 = ctypes::dirent; +/// `d_ino` + `d_off` + `d_reclen` + `d_type` +const DIRENT64_FIXED_SIZE: usize = 19; fn convert_name_to_array(name: &[u8]) -> [i8; 256] { let mut array = [0i8; 256]; @@ -612,39 +614,61 @@ fn convert_name_to_array(name: &[u8]) -> [i8; 256] { /// Read directory entries from a directory file descriptor. /// /// TODO: check errors, change 280 to a special value -pub unsafe fn sys_getdents64( - fd: c_int, - dirent: *mut LinuxDirent64, - count: ctypes::size_t, -) -> c_long { +pub unsafe fn sys_getdents64(fd: c_int, dirp: *mut LinuxDirent64, count: ctypes::size_t) -> c_long { debug!( - "sys_getdents64 <= fd: {}, dirent: {:p}, count: {}", - fd, dirent, count + "sys_getdents64 <= fd: {}, dirp: {:p}, count: {}", + fd, dirp, count ); syscall_body!(sys_getdents64, { - let expect_entries = count / 280; + if count < DIRENT64_FIXED_SIZE { + return Err(LinuxError::EINVAL); + } + let buf = unsafe { core::slice::from_raw_parts_mut(dirp, count) }; + // EBADFD handles here let dir = Directory::from_fd(fd)?; - let mut my_dirent: Vec = - (0..expect_entries).map(|_| DirEntry::default()).collect(); - - let n = dir.inner.lock().read_dir(&mut my_dirent)?; - - for (i, entry) in my_dirent.iter().enumerate() { - let linux_dirent = LinuxDirent64 { - d_ino: 1, - d_off: 280, - d_reclen: 280, - d_type: entry.entry_type() as u8, - d_name: convert_name_to_array(entry.name_as_bytes()), - }; - - unsafe { - core::ptr::write(dirent.add(i), linux_dirent); + // bytes written in buf + let mut written = 0; + + loop { + let mut entry = [DirEntry::default()]; + let offset = dir.inner.lock().entry_idx(); + let n = dir.inner.lock().read_dir(&mut entry)?; + if n == 0 { + return Ok(0); } + let entry = &entry[0]; + + let name = entry.name_as_bytes(); + let name_len = name.len(); + let entry_size = DIRENT64_FIXED_SIZE + name_len + 1; + + // buf not big enough to hold the entry + if written + entry_size > count { + debug!("buf not big enough"); + // revert the offset + dir.inner.lock().set_entry_idx(offset); + break; + } + + // write entry to buffer + let dirent: &mut LinuxDirent64 = + unsafe { &mut *(buf.as_mut_ptr().add(written) as *mut LinuxDirent64) }; + // 设置定长部分 + dirent.d_ino = 1; + dirent.d_off = offset as i64; + dirent.d_reclen = entry_size as u16; + dirent.d_type = entry.entry_type() as u8; + // 写入文件名 + dirent.d_name[..name_len].copy_from_slice(unsafe { + core::slice::from_raw_parts(name.as_ptr() as *const i8, name_len) + }); + dirent.d_name[name_len] = 0 as i8; + + written += entry_size; } - Ok(n * 280) + Ok(written as isize) }) } diff --git a/crates/axfs_vfs/Cargo.toml b/crates/axfs_vfs/Cargo.toml index 319dd8a8a..e5fe97e0c 100644 --- a/crates/axfs_vfs/Cargo.toml +++ b/crates/axfs_vfs/Cargo.toml @@ -14,5 +14,5 @@ default = [] [dependencies] log = "0.4" -bitflags = "2.2" +bitflags = "2.6" axerrno = { path = "../axerrno" } diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 0ca624616..6c15e6c64 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -44,7 +44,6 @@ memory_addr = "0.1.0" [dependencies.fatfs] git = "https://github.com/syswonder/rust-fatfs.git" -rev = "bf8ad02" optional = true default-features = false features = [ # no std diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index c71f49054..257ffccb6 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -338,6 +338,16 @@ impl Directory { Ok(n) } + /// Get the entry cursor of the directory. + pub fn entry_idx(&self) -> usize { + self.entry_idx + } + + /// Set the entry cursor of the directory. + pub fn set_entry_idx(&mut self, idx: usize) { + self.entry_idx = idx; + } + /// Rename a file or directory to a new name. /// Delete the original file if `old` already exists. /// From 663ad3c82706e056f44867b755e59e1ad3eaf6fd Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 10 Oct 2024 16:17:42 +0800 Subject: [PATCH 02/36] test: add test harness --- Cargo.lock | 765 ++++++++++++++++---------------- Cargo.toml | 2 +- apps/custom/harness/Cargo.toml | 13 + apps/custom/harness/src/main.rs | 139 ++++++ 4 files changed, 542 insertions(+), 377 deletions(-) create mode 100644 apps/custom/harness/Cargo.toml create mode 100644 apps/custom/harness/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 859e50f6a..5f1e3213c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aarch64-cpu" -version = "9.3.1" +version = "9.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb711c57d60565ba8f6523eb371e6243639617d817b4df1ae09af250af1af411" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" dependencies = [ "tock-registers", ] @@ -21,14 +21,14 @@ dependencies = [ "const-random", "once_cell", "version_check", - "zerocopy 0.7.32", + "zerocopy 0.7.35", ] [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -70,9 +70,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "arceos-shell" @@ -118,18 +118,18 @@ dependencies = [ [[package]] name = "atomic-polyfill" -version = "0.1.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axalloc" @@ -156,7 +156,7 @@ version = "0.1.0" dependencies = [ "axfs_vfs", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -165,7 +165,7 @@ version = "0.1.0" dependencies = [ "axfs_vfs", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -173,7 +173,7 @@ name = "axfs_vfs" version = "0.1.0" dependencies = [ "axerrno", - "bitflags 2.4.0", + "bitflags 2.6.0", "log", ] @@ -259,7 +259,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.77", "which", ] @@ -269,7 +269,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "lazy_static", @@ -282,7 +282,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.32", + "syn 2.0.77", "which", ] @@ -306,9 +306,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmap-allocator" @@ -320,34 +320,34 @@ dependencies = [ [[package]] name = "bitmaps" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "buddy_system_allocator" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f9365b6b0c9e1663ca4ca9440c00eda46bc85a3407070be8b5e0d8d1f29629" +checksum = "d44d578cadd17312c75e7d0ef489361f160ace58f7139aa32001fee1a51b89b5" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "capability" version = "0.1.0" dependencies = [ "axerrno", - "bitflags 2.4.0", + "bitflags 2.6.0", ] [[package]] @@ -358,12 +358,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -383,24 +384,23 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -409,15 +409,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -425,9 +425,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -436,18 +436,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.23" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.23" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstyle", "clap_lex", @@ -455,9 +455,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "cobs" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "const-default" @@ -487,9 +493,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core_detect" @@ -499,13 +505,13 @@ checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" [[package]] name = "crate_interface" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54b79db793871881b52a1eb5dc6fd869743122e34795f12db8e183dcb6a4f28" +checksum = "6af24c4862260a825484470f5526a91ad1031e04ab899be62478241231f62b46" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] @@ -546,52 +552,34 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -617,9 +605,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "defmt" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" +checksum = "a99dd22262668b887121d4672af5a64b238f026099f1a2a1b322066c9ecfe9e0" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -627,22 +615,22 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" +checksum = "e3a9f309eff1f79b3ebdf252954d90ae440599c26c2c553fe87a2d17195f2dcb" dependencies = [ "defmt-parser", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] name = "defmt-parser" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +checksum = "ff4a5fefe330e8d7f31b16a318f9ce81000d8e35e69b93eae154d16d2278f70f" dependencies = [ "thiserror", ] @@ -653,7 +641,7 @@ version = "0.1.0" dependencies = [ "driver_common", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -683,7 +671,7 @@ dependencies = [ "driver_common", "ixgbe-driver", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -703,7 +691,7 @@ dependencies = [ "driver_display", "driver_net", "log", - "spin 0.9.8", + "spin", "virtio-drivers", ] @@ -724,9 +712,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elf" @@ -781,23 +769,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -809,22 +786,22 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fatfs" version = "0.4.0" -source = "git+https://github.com/syswonder/rust-fatfs.git?rev=bf8ad02#bf8ad02bc6060728f6d3b81ee8428d731da4ca94" +source = "git+https://github.com/syswonder/rust-fatfs.git#a1d6da5cb6c046992396e11e9aca63d4b155431a" dependencies = [ "bitflags 1.3.2", "log", - "spin 0.9.8", + "spin", ] [[package]] name = "fdt-rs" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b" +checksum = "581d3afdd654deb68c19fcbe4bc411910cc64067d4a13d8637bda7722cb9c2ea" dependencies = [ "endian-type-rs", "fallible-iterator", - "memoffset 0.5.6", + "memoffset", "num-derive", "num-traits", "rustc_version 0.2.3", @@ -850,13 +827,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -867,15 +844,30 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "1.8.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "handler_table" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011878ffbfb4cbfcbd9fd799d1168a65897b0b0195fc55f9ec73d27f1685b1dd" +checksum = "e81913856cda07e1e7044a375043c2e4429ddb7d94a0d4ad10d4c27796ce4bd9" + +[[package]] +name = "harness" +version = "0.1.0" +dependencies = [ + "axstd", + "km-command", + "km-harness", + "log", + "ruxos_posix_api", +] [[package]] name = "hash32" @@ -886,43 +878,72 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapless" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", - "hash32", - "rustc_version 0.4.0", - "spin 0.9.8", + "hash32 0.2.1", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", "stable_deref_trait", ] [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -936,9 +957,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -946,13 +967,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi", - "rustix", - "windows-sys", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -975,9 +996,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "ixgbe-driver" @@ -993,43 +1014,62 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] [[package]] name = "kernel_guard" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5638cc8298368ef284589ad372890c849b520cccd50567d86a77a9232e68afdc" +checksum = "36172feaa47f9967efd3bc5fc77462899286e821de3de68c5fe88176a24d3a1f" dependencies = [ "cfg-if", "crate_interface", ] +[[package]] +name = "km-command" +version = "0.1.0" +dependencies = [ + "bitflags 2.6.0", + "heapless 0.8.0", + "postcard", + "serde", +] + +[[package]] +name = "km-harness" +version = "0.1.0" +source = "git+https://github.com/LJxTHUCS/km-harness.git#bc94ebe057ba482828baa8893e1b9cbe02677d3a" +dependencies = [ + "libafl_qemu_cmd", + "serde", +] + [[package]] name = "lazy_init" version = "0.1.0" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] @@ -1038,20 +1078,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "libafl_qemu_cmd" +version = "0.1.0" +source = "git+https://github.com/nine-point-eight-p/libafl_qemu_cmd#9873bf57108d4535d8bf5f7c458039b88a649367" + [[package]] name = "libc" -version = "0.2.147" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "winapi", + "windows-targets", ] [[package]] @@ -1060,15 +1105,15 @@ version = "0.1.0" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1076,9 +1121,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lwip_rust" @@ -1096,9 +1141,9 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -1109,26 +1154,17 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "memory_addr" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b53e2ffbb2aaf3b738bd38ddd7ca8c511e47e05cd03fc09eacb7c5fa1285e5" +checksum = "359639f2591c78370034feaa79ebd114516d04ba642a89bb68b2b806ef2bc2f6" [[package]] name = "micromath" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39617bc909d64b068dcffd0e3e31679195b5576d0c83fadc52690268cc2b2b55" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" [[package]] name = "minimal-lexical" @@ -1174,23 +1210,13 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.5.11" @@ -1213,15 +1239,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "page_table" @@ -1237,7 +1263,7 @@ name = "page_table_entry" version = "0.1.0" dependencies = [ "aarch64-cpu", - "bitflags 2.4.0", + "bitflags 2.6.0", "log", "memory_addr", "x86_64", @@ -1245,9 +1271,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "peeking_take_while" @@ -1262,7 +1288,7 @@ dependencies = [ "cfg-if", "kernel_guard", "percpu_macros", - "spin 0.9.8", + "spin", "x86", ] @@ -1272,14 +1298,14 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -1290,33 +1316,47 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "heapless 0.7.17", + "serde", +] + [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] [[package]] name = "prettyplease" -version = "0.2.12" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] @@ -1357,18 +1397,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -1418,18 +1458,18 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.0.1" +version = "11.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" +checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", ] [[package]] name = "rayon" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1437,21 +1477,19 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1461,9 +1499,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1472,9 +1510,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "riscv" @@ -1516,31 +1554,31 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.18", + "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "rux9p" @@ -1554,7 +1592,7 @@ dependencies = [ "ruxdriver", "ruxfs", "ruxnet", - "spin 0.9.8", + "spin", ] [[package]] @@ -1603,7 +1641,7 @@ dependencies = [ "flatten_objects", "lazy_static", "log", - "spin 0.9.8", + "spin", ] [[package]] @@ -1653,7 +1691,7 @@ version = "0.1.0" dependencies = [ "ahash", "axerrno", - "bitflags 2.4.0", + "bitflags 2.6.0", "lazy_static", "log", "memory_addr", @@ -1670,7 +1708,7 @@ dependencies = [ "arm_pl011", "axalloc", "axlog", - "bitflags 2.4.0", + "bitflags 2.6.0", "cfg-if", "crate_interface", "dtb", @@ -1685,7 +1723,7 @@ dependencies = [ "page_table_entry", "percpu", "ratio", - "raw-cpuid 11.0.1", + "raw-cpuid 11.1.0", "riscv", "ruxconfig", "sbi-rt", @@ -1742,7 +1780,7 @@ dependencies = [ "ruxhal", "ruxtask", "smoltcp", - "spin 0.9.8", + "spin", ] [[package]] @@ -1772,7 +1810,7 @@ dependencies = [ "axlog", "axsync", "bindgen 0.66.1", - "bitflags 2.4.0", + "bitflags 2.6.0", "cfg-if", "crate_interface", "elf", @@ -1790,7 +1828,7 @@ dependencies = [ "ruxnet", "ruxruntime", "ruxtask", - "spin 0.9.8", + "spin", "spinlock", "static_assertions", ] @@ -1844,9 +1882,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1896,9 +1934,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "semver-parser" @@ -1908,40 +1946,41 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.185" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab_allocator" @@ -1959,17 +1998,11 @@ dependencies = [ "byteorder", "cfg-if", "defmt", - "heapless", + "heapless 0.7.17", "log", "managed", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -2025,9 +2058,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -2036,33 +2069,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.77", ] [[package]] @@ -2105,15 +2127,15 @@ checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -2135,20 +2157,20 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe_unwrap" @@ -2158,9 +2180,9 @@ checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtio-drivers" @@ -2169,7 +2191,7 @@ source = "git+https://github.com/syswonder/virtio-drivers.git?rev=62dbe5a#62dbe5 dependencies = [ "bitflags 1.3.2", "log", - "zerocopy 0.6.3", + "zerocopy 0.6.6", ] [[package]] @@ -2198,20 +2220,14 @@ checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2220,34 +2236,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2255,28 +2272,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -2284,73 +2301,62 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "winapi-util" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-sys 0.59.0", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "winapi", + "windows-targets", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -2359,60 +2365,66 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "x2apic" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "547152b57dd1ae0ce7a4ef1c6470f6039aa7ed22e2179d5bc4f3eda1304e0db3" +checksum = "cbcd582541cbb8ef1dfc24a3c849a64ff074b1b512af723ad90056558d424602" dependencies = [ "bit", "bitflags 1.3.2", @@ -2434,53 +2446,54 @@ dependencies = [ [[package]] name = "x86_64" -version = "0.14.10" +version = "0.14.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" +checksum = "96cb6fd45bfeab6a5055c5bffdb08768bd0c069f1d946debe585bbb380a7c062" dependencies = [ "bit_field", - "bitflags 1.3.2", + "bitflags 2.6.0", "rustversion", "volatile 0.4.6", ] [[package]] name = "zerocopy" -version = "0.6.3" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b9c234616391070b0b173963ebc65a9195068e7ed3731c6edac2ec45ebe106" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ "byteorder", - "zerocopy-derive 0.6.3", + "zerocopy-derive 0.6.6", ] [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.32", + "byteorder", + "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy-derive" -version = "0.6.3" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f7f3a471f98d0a61c34322fbbfd10c384b07687f680d4119813713f72308d91" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index c1500efeb..df0512a6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ members = [ "apps/display/basic_painting", "apps/display/draw_map", - "apps/fs/shell", + "apps/fs/shell", "apps/custom/harness", ] [profile.release] diff --git a/apps/custom/harness/Cargo.toml b/apps/custom/harness/Cargo.toml new file mode 100644 index 000000000..27362d9f5 --- /dev/null +++ b/apps/custom/harness/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "harness" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axstd = { path = "../../../ulib/axstd" } +ruxos_posix_api = { path = "../../../api/ruxos_posix_api", features = ["fs"] } +km-harness = { git = "https://github.com/LJxTHUCS/km-harness.git" } +km-command = { path = "../../../../../../framework/km-command", features = ["harness", "postcard"] } +log = "0.4" \ No newline at end of file diff --git a/apps/custom/harness/src/main.rs b/apps/custom/harness/src/main.rs new file mode 100644 index 000000000..0fdd10eb9 --- /dev/null +++ b/apps/custom/harness/src/main.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +use axstd::println; +use core::{clone::Clone, format_args, panic}; +use core::{ffi::c_void, mem::size_of}; +use km_command::{fs::LibcDirent, FromBytes}; +use km_harness::{executor, harness_command, Command, Harness, MemPort}; +use ruxos_posix_api::{ + ctypes::dirent, sys_chdir, sys_close, sys_dup, sys_fstat, sys_getcwd, sys_getdents64, + sys_mkdirat, sys_openat, sys_unlinkat, +}; + +/// Size of harness buffer. +const HARNESS_BUF_SIZE: usize = 4096; + +/// Buffer for checker to write commands to. +#[link_section = ".data"] +static mut CMD_BUF: [u8; 4096] = [0; 4096]; + +/// Buffer for checker to read extra data from. +#[link_section = ".data"] +static mut OUTPUT_BUF: [u8; 4096] = [0; 4096]; + +/// Buffer for checker to read return value from. +#[link_section = ".data"] +static mut RETV_BUF: [u8; size_of::()] = [0; size_of::()]; + +harness_command!(km_command::fs, Openat, { + // Transfer the path to CStr. + let mut path = get!(path).clone(); + path.push('\0').unwrap(); + sys_openat( + get!(dirfd) as usize, + path.as_ptr() as *const i8, + get!(flags).bits() as i32, + get!(mode).bits(), + ) as isize +}); + +harness_command!(km_command::fs, Close, { + sys_close(get!(fd) as i32) as isize +}); + +harness_command!(km_command::fs, Fstat, { + sys_fstat(get!(fd) as i32, output!().as_mut_ptr() as *mut c_void) as isize +}); + +harness_command!(km_command::fs, Getdents1, { + // only want to get one directory entry per read. + // So we need to try buffer size from a very small value + // until it's just enough for one directory entry. + let mut buf_size = LibcDirent::ONE_DIRENT_BUF_SIZE; + loop { + let ret = unsafe { + sys_getdents64( + get!(fd) as i32, + output!().as_mut_ptr() as *mut dirent, + buf_size, + ) + }; + if ret >= 0 { + break ret as isize; + } + buf_size += 1; + } +}); + +// Not supported by fatfs +harness_command!(km_command::fs, Linkat, { 0 }); + +harness_command!(km_command::fs, Unlinkat, { + // Transfer the path to CStr. + let mut path = get!(path).clone(); + path.push('\0').unwrap(); + sys_unlinkat( + get!(dirfd) as i32, + path.as_ptr() as *const i8, + get!(flags).bits() as i32, + ) as isize +}); + +harness_command!(km_command::fs, Mkdirat, { + // Transfer the path to CStr. + let mut path = get!(path).clone(); + path.push('\0').unwrap(); + sys_mkdirat( + get!(dirfd) as i32, + path.as_ptr() as *const i8, + get!(mode).bits(), + ) as isize +}); + +harness_command!(km_command::fs, Getcwd, { + sys_getcwd(output!().as_mut_ptr() as *mut i8, output!().len()) as isize +}); + +harness_command!(km_command::fs, Chdir, { + // Transfer the path to CStr. + let mut path = get!(path).clone(); + path.push('\0').unwrap(); + sys_chdir(path.as_ptr() as *const i8) as isize +}); + +harness_command!(km_command::fs, Dup, { + sys_dup(get!(oldfd) as i32) as isize +}); + +harness_command!(km_command, Nop, { 0 }); + +// Define an executor +executor!( + FsSyscallExecutor, + Openat, + Close, + Fstat, + Getdents1, + Linkat, + Unlinkat, + Mkdirat, + Getcwd, + Chdir, + Dup, + Nop +); + +#[no_mangle] +fn main() { + println!("CMD_BUF at {:p}", unsafe { CMD_BUF.as_ptr() }); + println!("RETV_BUF at {:p}", unsafe { RETV_BUF.as_ptr() }); + println!("OUTPUT_BUF at {:p}", unsafe { OUTPUT_BUF.as_ptr() }); + let mut harness = Harness::::new( + unsafe { MemPort::new(&CMD_BUF, &mut RETV_BUF, &mut OUTPUT_BUF) }, + FsSyscallExecutor, + ); + loop { + harness.step(); + } +} From 767d892dff1a273af8676b42836cc1758dfb1059 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 16 Oct 2024 16:56:55 +0800 Subject: [PATCH 03/36] fix: openat directory flag --- api/ruxos_posix_api/src/imp/fs.rs | 49 +++++++++++++++++++------------ modules/ruxfs/src/fops.rs | 21 +++++++++++-- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 74ad04b5a..c471b47f6 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -15,7 +15,7 @@ use axio::{PollState, SeekFrom}; use axsync::Mutex; use ruxfdtable::{FileLike, RuxStat}; use ruxfs::{ - api::set_current_dir, + api::{current_dir, set_current_dir}, fops::{DirEntry, OpenOptions}, }; @@ -215,30 +215,41 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> /// Open a file under a specific dir pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { let path = char_ptr_to_absolute_path(path); + let path = path?; let fd: c_int = fd as c_int; - debug!("sys_openat <= {}, {:?}, {:#o}, {:#o}", fd, path, flags, mode); + debug!( + "sys_openat <= {}, {:?}, {:#o}, {:#o}", + fd, path, flags, mode + ); syscall_body!(sys_openat, { let options = flags_to_options(flags, mode); - if (flags as u32) & ctypes::O_DIRECTORY != 0 { - let dir = if fd == ctypes::AT_FDCWD { - ruxfs::fops::Directory::open_dir(&path?, &options)? + if fd == ctypes::AT_FDCWD { + let is_dir = ruxfs::fops::Directory::get_child_attr(&path)?.is_dir(); + // O_DIRECTORY flag is set but the path is not a directory, return ENOTDIR + if (flags as u32 & ctypes::O_DIRECTORY != 0) && !is_dir { + return Err(LinuxError::ENOTDIR); + } + if is_dir { + let dir = ruxfs::fops::Directory::open_dir(&path, &options)?; + Directory::new(dir).add_to_fd_table() } else { - Directory::from_fd(fd)? - .inner - .lock() - .open_dir_at(&path?, &options)? - }; - Directory::new(dir).add_to_fd_table() + let file = ruxfs::fops::File::open(&path, &options)?; + File::new(file).add_to_fd_table() + } } else { - let file = if fd == ctypes::AT_FDCWD { - ruxfs::fops::File::open(&path?, &options)? + let dir = Directory::from_fd(fd)?; + let is_dir = dir.inner.lock().get_child_attr_at(&path)?.is_dir(); + // O_DIRECTORY flag is set but the path is not a directory, return ENOTDIR + if (flags as u32 & ctypes::O_DIRECTORY != 0) && !is_dir { + return Err(LinuxError::ENOTDIR); + } + if is_dir { + let dir = dir.inner.lock().open_dir_at(&path, &options)?; + Directory::new(dir).add_to_fd_table() } else { - Directory::from_fd(fd)? - .inner - .lock() - .open_file_at(&path?, &options)? - }; - File::new(file).add_to_fd_table() + let file = dir.inner.lock().open_file_at(&path, &options)?; + File::new(file).add_to_fd_table() + } } }) } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 257ffccb6..3ffdf1452 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -10,10 +10,10 @@ //! Low-level filesystem operations. use axerrno::{ax_err, ax_err_type, AxResult}; -use axfs_vfs::{VfsError, VfsNodeRef}; +use axfs_vfs::{VfsError, VfsNodeAttr, VfsNodeRef}; use axio::SeekFrom; use capability::{Cap, WithCap}; -use core::fmt; +use core::{fmt, ops}; #[cfg(feature = "myfs")] pub use crate::dev::Disk; @@ -278,6 +278,11 @@ impl Directory { }) } + fn _get_child_attr_at(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { + let node = crate::root::lookup(dir, path)?; + Ok(node.get_attr()?) + } + fn access_at(&self, path: &str) -> AxResult> { if path.starts_with('/') { Ok(None) @@ -286,6 +291,18 @@ impl Directory { } } + /// Gets the file attributes of the file at the path relative to current directory. + /// Returns a [`FileAttr`] object. + pub fn get_child_attr(path: &str) -> AxResult { + Self::_get_child_attr_at(None, path) + } + + /// Gets the file attributes of the file at the path relative to this directory. + /// Returns a [`FileAttr`] object. + pub fn get_child_attr_at(&self, path: &str) -> AxResult { + Self::_get_child_attr_at(self.access_at(path)?, path) + } + /// Opens a directory at the path relative to the current directory. /// Returns a [`Directory`] object. pub fn open_dir(path: &str, opts: &OpenOptions) -> AxResult { From 0fdd5af3c3d7e57563d5e4c72498fe4a304ecc4f Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 16 Oct 2024 17:32:19 +0800 Subject: [PATCH 04/36] fix: open with absolute path --- api/ruxos_posix_api/src/imp/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index c471b47f6..617c1fe72 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -215,15 +215,15 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> /// Open a file under a specific dir pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { let path = char_ptr_to_absolute_path(path); - let path = path?; let fd: c_int = fd as c_int; debug!( "sys_openat <= {}, {:?}, {:#o}, {:#o}", fd, path, flags, mode ); syscall_body!(sys_openat, { + let path = path?; let options = flags_to_options(flags, mode); - if fd == ctypes::AT_FDCWD { + if fd == ctypes::AT_FDCWD || path.starts_with("/") { let is_dir = ruxfs::fops::Directory::get_child_attr(&path)?.is_dir(); // O_DIRECTORY flag is set but the path is not a directory, return ENOTDIR if (flags as u32 & ctypes::O_DIRECTORY != 0) && !is_dir { From b93320dee0cdebfbcddc080c3c9628bbb30c1e29 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 16 Oct 2024 21:16:59 +0800 Subject: [PATCH 05/36] refactor: define AbsPath and RelPath type --- crates/axfs_vfs/src/lib.rs | 19 ++++----- crates/axfs_vfs/src/macros.rs | 6 +-- crates/axfs_vfs/src/path.rs | 80 ++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/crates/axfs_vfs/src/lib.rs b/crates/axfs_vfs/src/lib.rs index cc0f87870..15f5db2ab 100644 --- a/crates/axfs_vfs/src/lib.rs +++ b/crates/axfs_vfs/src/lib.rs @@ -54,6 +54,7 @@ pub mod path; use alloc::sync::Arc; use axerrno::{ax_err, AxError, AxResult}; +use path::{AbsPath, RelPath}; pub use self::structs::{FileSystemInfo, VfsDirEntry, VfsNodeAttr, VfsNodePerm, VfsNodeType}; @@ -69,7 +70,7 @@ pub type VfsResult = AxResult; /// Filesystem operations. pub trait VfsOps: Send + Sync { /// Do something when the filesystem is mounted. - fn mount(&self, _path: &str, _mount_point: VfsNodeRef) -> VfsResult { + fn mount(&self, _path: &AbsPath, _mount_point: VfsNodeRef) -> VfsResult { Ok(()) } @@ -143,19 +144,19 @@ pub trait VfsNodeOps: Send + Sync { /// Lookup the node with given `path` in the directory. /// /// Return the node if found. - fn lookup(self: Arc, _path: &str) -> VfsResult { + fn lookup(self: Arc, _path: &RelPath) -> VfsResult { ax_err!(Unsupported) } /// Create a new node with the given `path` in the directory /// /// Return [`Ok(())`](Ok) if it already exists. - fn create(&self, _path: &str, _ty: VfsNodeType) -> VfsResult { + fn create(&self, _path: &RelPath, _ty: VfsNodeType) -> VfsResult { ax_err!(Unsupported) } /// Remove the node with the given `path` in the directory. - fn remove(&self, _path: &str) -> VfsResult { + fn remove(&self, _path: &RelPath) -> VfsResult { ax_err!(Unsupported) } @@ -165,7 +166,7 @@ pub trait VfsNodeOps: Send + Sync { } /// Renames or moves existing file or directory. - fn rename(&self, _src_path: &str, _dst_path: &str) -> VfsResult { + fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult { ax_err!(Unsupported) } @@ -184,18 +185,14 @@ pub trait VfsNodeOps: Send + Sync { /// implementor may provide a more efficient impl. /// /// Return [`Ok(())`](Ok) if already exists. - fn create_recursive(&self, path: &str, ty: VfsNodeType) -> VfsResult { - if path.starts_with('/') { - return ax_err!(InvalidInput); - } - let path = path.trim_end_matches('/'); + fn create_recursive(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { for (i, c) in path.char_indices() { let part = if c == '/' { unsafe { path.get_unchecked(..i) } } else { continue; }; - match self.create(part, VfsNodeType::Dir) { + match self.create(&RelPath::new(part), VfsNodeType::Dir) { Ok(()) | Err(AxError::AlreadyExists) => {} err @ Err(_) => return err, } diff --git a/crates/axfs_vfs/src/macros.rs b/crates/axfs_vfs/src/macros.rs index 99f8ab3e3..859cf0408 100644 --- a/crates/axfs_vfs/src/macros.rs +++ b/crates/axfs_vfs/src/macros.rs @@ -46,16 +46,16 @@ macro_rules! impl_vfs_non_dir_default { () => { fn lookup( self: $crate::__priv::Arc, - _path: &str, + _path: &$crate::path::RelPath, ) -> $crate::VfsResult<$crate::VfsNodeRef> { $crate::__priv::ax_err!(NotADirectory) } - fn create(&self, _path: &str, _ty: $crate::VfsNodeType) -> $crate::VfsResult { + fn create(&self, _path: &$crate::path::RelPath, _ty: $crate::VfsNodeType) -> $crate::VfsResult { $crate::__priv::ax_err!(NotADirectory) } - fn remove(&self, _path: &str) -> $crate::VfsResult { + fn remove(&self, _path: &$crate::path::RelPath) -> $crate::VfsResult { $crate::__priv::ax_err!(NotADirectory) } diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index 37faf3555..894f52a7c 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -9,7 +9,10 @@ //! Utilities for path manipulation. -use alloc::string::String; +use alloc::{ + borrow::{Cow, ToOwned}, + string::String, +}; /// Returns the canonical form of the path with all intermediate components /// normalized. @@ -60,6 +63,81 @@ pub fn canonicalize(path: &str) -> String { buf } +/// CANONICALIZED absolute path type, starting with '/'. +/// +/// Using `Cow` type to avoid unnecessary allocations. +#[derive(Debug)] +pub struct AbsPath<'a>(Cow<'a, str>); + +impl<'a> AbsPath<'a> { + /// Simply wrap a string into a `AbsPath`. + pub fn new(path: &'a str) -> Self { + Self(Cow::Borrowed(path)) + } + + /// Parse and canonicalize an absolute path from a string. + pub fn new_canonicalized(path: &str) -> Self { + if !path.starts_with('/') { + Self(Cow::Owned(canonicalize(&("/".to_owned() + path)))) + } else { + Self(Cow::Owned(canonicalize(path))) + } + } +} + +impl core::ops::Deref for AbsPath<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::fmt::Display for AbsPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + +/// CANONICALIZED relative path type, no starting '.' or '/'. +/// possibly starts with '..'. +/// +/// Valid examples: +/// - "" +/// - ".." +/// - "../b" +/// - "../.." +/// - "a/b/c" +/// +/// Using `Cow` type to avoid unnecessary allocations. +pub struct RelPath<'a>(Cow<'a, str>); + +impl<'a> RelPath<'a> { + /// Wrap a string into a `RelPath`. + pub fn new(path: &'a str) -> Self { + Self(Cow::Borrowed(path)) + } + + /// Parse and canonicalize a relative path from a string. + pub fn new_canonicalized(path: &str) -> Self { + Self(Cow::Owned(canonicalize(path.trim_start_matches("/")))) + } +} + +impl core::ops::Deref for RelPath<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::fmt::Display for RelPath<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + #[cfg(test)] mod tests { use super::*; From b26a0674b84109a614768639a192f88cc8e76f35 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 16 Oct 2024 21:18:22 +0800 Subject: [PATCH 06/36] refactor: devfs use new path defs --- crates/axfs_devfs/src/dir.rs | 32 +++++++++++------------ crates/axfs_devfs/src/lib.rs | 4 +-- crates/axfs_devfs/src/tests.rs | 48 ++++++++++++++++------------------ 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/crates/axfs_devfs/src/dir.rs b/crates/axfs_devfs/src/dir.rs index 2245fcb49..6ea67bfb8 100644 --- a/crates/axfs_devfs/src/dir.rs +++ b/crates/axfs_devfs/src/dir.rs @@ -9,6 +9,7 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; +use axfs_vfs::path::RelPath; use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; @@ -57,11 +58,10 @@ impl VfsNodeOps for DirNode { self.parent.read().upgrade() } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); let node = match name { - "" | "." => Ok(self.clone() as VfsNodeRef), - ".." => self.parent().ok_or(VfsError::NotFound), + "" => Ok(self.clone() as VfsNodeRef), _ => self .children .read() @@ -69,9 +69,8 @@ impl VfsNodeOps for DirNode { .cloned() .ok_or(VfsError::NotFound), }?; - if let Some(rest) = rest { - node.lookup(rest) + node.lookup(&rest) } else { Ok(node) } @@ -96,19 +95,19 @@ impl VfsNodeOps for DirNode { Ok(dirents.len()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { log::debug!("create {:?} at devfs: {}", ty, path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.create(rest, ty), - ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty), + "" | "." => self.create(&rest, ty), + ".." => self.parent().ok_or(VfsError::NotFound)?.create(&rest, ty), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .create(rest, ty), + .create(&rest, ty), } } else if name.is_empty() || name == "." || name == ".." { Ok(()) // already exists @@ -117,19 +116,19 @@ impl VfsNodeOps for DirNode { } } - fn remove(&self, path: &str) -> VfsResult { + fn remove(&self, path: &RelPath) -> VfsResult { log::debug!("remove at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.remove(rest), - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest), + "" | "." => self.remove(&rest), + ".." => self.parent().ok_or(VfsError::NotFound)?.remove(&rest), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .remove(rest), + .remove(&rest), } } else { Err(VfsError::PermissionDenied) // do not support to remove nodes dynamically @@ -139,9 +138,8 @@ impl VfsNodeOps for DirNode { axfs_vfs::impl_vfs_dir_default! {} } -fn split_path(path: &str) -> (&str, Option<&str>) { - let trimmed_path = path.trim_start_matches('/'); - trimmed_path.find('/').map_or((trimmed_path, None), |n| { - (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) +fn split_path<'a>(path: &'a RelPath) -> (&'a str, Option>) { + path.find('/').map_or((path, None), |n| { + (&path[..n], Some(RelPath::new(&path[n + 1..]))) }) } diff --git a/crates/axfs_devfs/src/lib.rs b/crates/axfs_devfs/src/lib.rs index 3334fb777..fef7e118c 100644 --- a/crates/axfs_devfs/src/lib.rs +++ b/crates/axfs_devfs/src/lib.rs @@ -29,7 +29,7 @@ pub use self::random::RandomDev; pub use self::zero::ZeroDev; use alloc::sync::Arc; -use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{path::AbsPath, VfsNodeRef, VfsOps, VfsResult}; use spin::once::Once; /// A device filesystem that implements [`axfs_vfs::VfsOps`]. @@ -61,7 +61,7 @@ impl DeviceFileSystem { } impl VfsOps for DeviceFileSystem { - fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult { + fn mount(&self, _path: &AbsPath, mount_point: VfsNodeRef) -> VfsResult { if let Some(parent) = mount_point.parent() { self.root.set_parent(Some(self.parent.call_once(|| parent))); } else { diff --git a/crates/axfs_devfs/src/tests.rs b/crates/axfs_devfs/src/tests.rs index 71c8007ad..815dab6a2 100644 --- a/crates/axfs_devfs/src/tests.rs +++ b/crates/axfs_devfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{path::RelPath, VfsError, VfsNodeType, VfsResult}; use crate::*; @@ -21,24 +21,20 @@ fn test_devfs_ops(devfs: &DeviceFileSystem) -> VfsResult { assert!(root.get_attr()?.is_dir()); assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); assert_eq!( - root.clone().lookup("urandom").err(), + root.clone().lookup(&RelPath::new_canonicalized("urandom")).err(), Some(VfsError::NotFound) ); - assert_eq!( - root.clone().lookup("zero/").err(), - Some(VfsError::NotADirectory) - ); - let node = root.lookup("////null")?; + let node = root.lookup(&RelPath::new_canonicalized("////null"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); assert_eq!(node.read_at(0, &mut buf)?, 0); assert_eq!(buf, [1; N]); assert_eq!(node.write_at(N as _, &buf)?, N); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); + assert_eq!(node.lookup(&RelPath::new_canonicalized("/")).err(), Some(VfsError::NotADirectory)); - let node = devfs.root_dir().lookup(".///.//././/.////zero")?; + let node = devfs.root_dir().lookup(&RelPath::new_canonicalized(".///.//././/.////zero"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); @@ -46,22 +42,22 @@ fn test_devfs_ops(devfs: &DeviceFileSystem) -> VfsResult { assert_eq!(buf, [0; N]); assert_eq!(node.write_at(0, &buf)?, N); - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; + let foo = devfs.root_dir().lookup(&RelPath::new_canonicalized(".///.//././/.////foo"))?; assert!(foo.get_attr()?.is_dir()); assert_eq!( foo.read_at(10, &mut buf).err(), Some(VfsError::IsADirectory) ); assert!(Arc::ptr_eq( - &foo.clone().lookup("/f2")?, - &devfs.root_dir().lookup(".//./foo///f2")?, + &foo.clone().lookup(&RelPath::new_canonicalized("/f2"))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo///f2"))?, )); assert_eq!( - foo.clone().lookup("/bar//f1")?.get_attr()?.file_type(), + foo.clone().lookup(&RelPath::new_canonicalized("/bar//f1"))?.get_attr()?.file_type(), VfsNodeType::CharDevice ); assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), + foo.lookup(&RelPath::new_canonicalized("/bar///"))?.get_attr()?.file_type(), VfsNodeType::Dir ); @@ -72,29 +68,29 @@ fn test_get_parent(devfs: &DeviceFileSystem) -> VfsResult { let root = devfs.root_dir(); assert!(root.parent().is_none()); - let node = root.clone().lookup("null")?; + let node = root.clone().lookup(&RelPath::new_canonicalized("null"))?; assert!(node.parent().is_none()); - let node = root.clone().lookup(".//foo/bar")?; + let node = root.clone().lookup(&RelPath::new_canonicalized(".//foo/bar"))?; assert!(node.parent().is_some()); let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); + assert!(Arc::ptr_eq(&parent, &root.clone().lookup(&RelPath::new_canonicalized("foo"))?)); + assert!(parent.lookup(&RelPath::new_canonicalized("bar")).is_ok()); - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); + let node = root.clone().lookup(&RelPath::new_canonicalized("foo/.."))?; + assert!(Arc::ptr_eq(&node, &root.clone().lookup(&RelPath::new_canonicalized("."))?)); assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, + &root.clone().lookup(&RelPath::new_canonicalized("/foo/.."))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo/././bar/../.."))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, + &root.clone().lookup(&RelPath::new_canonicalized("././/foo//./../foo//bar///..//././"))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo/"))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f2")?, - &root.lookup("foo/.//f2")?, + &root.clone().lookup(&RelPath::new_canonicalized("///foo//bar///../f2"))?, + &root.lookup(&RelPath::new_canonicalized("foo/.//f2"))?, )); Ok(()) From cacbb1f3af79c263ad2509e6d86f28a35c1b5349 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 24 Oct 2024 10:37:00 +0800 Subject: [PATCH 07/36] refactor: ramfs use new path defs --- crates/axfs_ramfs/src/dir.rs | 64 +++++++++++--------------- crates/axfs_ramfs/src/lib.rs | 4 +- crates/axfs_ramfs/src/tests.rs | 82 ++++++++++++++++------------------ 3 files changed, 68 insertions(+), 82 deletions(-) diff --git a/crates/axfs_ramfs/src/dir.rs b/crates/axfs_ramfs/src/dir.rs index e685bd255..3740d2110 100644 --- a/crates/axfs_ramfs/src/dir.rs +++ b/crates/axfs_ramfs/src/dir.rs @@ -11,6 +11,7 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; use alloc::{string::String, vec::Vec}; +use axfs_vfs::path::RelPath; use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; @@ -87,11 +88,10 @@ impl VfsNodeOps for DirNode { self.parent.read().upgrade() } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); let node = match name { - "" | "." => Ok(self.clone() as VfsNodeRef), - ".." => self.parent().ok_or(VfsError::NotFound), + "" => Ok(self.clone() as VfsNodeRef), _ => self .children .read() @@ -99,9 +99,8 @@ impl VfsNodeOps for DirNode { .cloned() .ok_or(VfsError::NotFound), }?; - if let Some(rest) = rest { - node.lookup(rest) + node.lookup(&rest) } else { Ok(node) } @@ -126,48 +125,40 @@ impl VfsNodeOps for DirNode { Ok(dirents.len()) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at ramfs: {}", ty, path); + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + log::debug!("create {:?} at devfs: {}", ty, path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.create(rest, ty), - ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty), - _ => { - let subdir = self - .children - .read() - .get(name) - .ok_or(VfsError::NotFound)? - .clone(); - subdir.create(rest, ty) - } + ".." => self.parent().ok_or(VfsError::NotFound)?.create(&rest, ty), + _ => self + .children + .read() + .get(name) + .ok_or(VfsError::NotFound)? + .create(&rest, ty), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name.is_empty() || name == ".." { Ok(()) // already exists } else { self.create_node(name, ty) } } - fn remove(&self, path: &str) -> VfsResult { - log::debug!("remove at ramfs: {}", path); + fn remove(&self, path: &RelPath) -> VfsResult { + log::debug!("remove at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.remove(rest), - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest), - _ => { - let subdir = self - .children - .read() - .get(name) - .ok_or(VfsError::NotFound)? - .clone(); - subdir.remove(rest) - } + ".." => self.parent().ok_or(VfsError::NotFound)?.remove(&rest), + _ => self + .children + .read() + .get(name) + .ok_or(VfsError::NotFound)? + .remove(&rest), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name.is_empty() || name == ".." { Err(VfsError::InvalidInput) // remove '.' or '.. } else { self.remove_node(name) @@ -177,9 +168,8 @@ impl VfsNodeOps for DirNode { axfs_vfs::impl_vfs_dir_default! {} } -fn split_path(path: &str) -> (&str, Option<&str>) { - let trimmed_path = path.trim_start_matches('/'); - trimmed_path.find('/').map_or((trimmed_path, None), |n| { - (&trimmed_path[..n], Some(&trimmed_path[n + 1..])) +fn split_path<'a>(path: &'a RelPath) -> (&'a str, Option>) { + path.find('/').map_or((path, None), |n| { + (&path[..n], Some(RelPath::new(&path[n + 1..]))) }) } diff --git a/crates/axfs_ramfs/src/lib.rs b/crates/axfs_ramfs/src/lib.rs index 45f11b66b..9dc159b2a 100644 --- a/crates/axfs_ramfs/src/lib.rs +++ b/crates/axfs_ramfs/src/lib.rs @@ -25,7 +25,7 @@ pub use self::dir::DirNode; pub use self::file::FileNode; use alloc::sync::Arc; -use axfs_vfs::{VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{path::AbsPath, VfsNodeRef, VfsOps, VfsResult}; use spin::once::Once; /// A RAM filesystem that implements [`axfs_vfs::VfsOps`]. @@ -50,7 +50,7 @@ impl RamFileSystem { } impl VfsOps for RamFileSystem { - fn mount(&self, _path: &str, mount_point: VfsNodeRef) -> VfsResult { + fn mount(&self, _path: &AbsPath, mount_point: VfsNodeRef) -> VfsResult { if let Some(parent) = mount_point.parent() { self.root.set_parent(Some(self.parent.call_once(|| parent))); } else { diff --git a/crates/axfs_ramfs/src/tests.rs b/crates/axfs_ramfs/src/tests.rs index 1e767635e..ac5153558 100644 --- a/crates/axfs_ramfs/src/tests.rs +++ b/crates/axfs_ramfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{path::RelPath, VfsError, VfsNodeType, VfsResult}; use crate::*; @@ -22,15 +22,11 @@ fn test_ramfs_ops(devfs: &RamFileSystem) -> VfsResult { assert!(root.get_attr()?.is_dir()); assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); assert_eq!( - root.clone().lookup("urandom").err(), + root.clone().lookup(&RelPath::new_canonicalized("urandom")).err(), Some(VfsError::NotFound) ); - assert_eq!( - root.clone().lookup("f1/").err(), - Some(VfsError::NotADirectory) - ); - let node = root.lookup("////f1")?; + let node = root.lookup(&RelPath::new_canonicalized("////f1"))?; assert_eq!(node.get_attr()?.file_type(), VfsNodeType::File); assert!(!node.get_attr()?.is_dir()); assert_eq!(node.get_attr()?.size(), 0); @@ -41,24 +37,24 @@ fn test_ramfs_ops(devfs: &RamFileSystem) -> VfsResult { assert_eq!(node.read_at(0, &mut buf)?, N); assert_eq!(buf[..N_HALF], [0; N_HALF]); assert_eq!(buf[N_HALF..], [1; N_HALF]); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); + assert_eq!(node.lookup(&RelPath::new_canonicalized("/")).err(), Some(VfsError::NotADirectory)); - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; + let foo = devfs.root_dir().lookup(&RelPath::new_canonicalized(".///.//././/.////foo"))?; assert!(foo.get_attr()?.is_dir()); assert_eq!( foo.read_at(10, &mut buf).err(), Some(VfsError::IsADirectory) ); assert!(Arc::ptr_eq( - &foo.clone().lookup("/f3")?, - &devfs.root_dir().lookup(".//./foo///f3")?, + &foo.clone().lookup(&RelPath::new_canonicalized("/f3"))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo///f3"))?, )); assert_eq!( - foo.clone().lookup("/bar//f4")?.get_attr()?.file_type(), + foo.clone().lookup(&RelPath::new_canonicalized("/bar//f4"))?.get_attr()?.file_type(), VfsNodeType::File ); assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), + foo.lookup(&RelPath::new_canonicalized("/bar///"))?.get_attr()?.file_type(), VfsNodeType::Dir ); @@ -69,29 +65,29 @@ fn test_get_parent(devfs: &RamFileSystem) -> VfsResult { let root = devfs.root_dir(); assert!(root.parent().is_none()); - let node = root.clone().lookup("f1")?; + let node = root.clone().lookup(&RelPath::new_canonicalized("f1"))?; assert!(node.parent().is_none()); - let node = root.clone().lookup(".//foo/bar")?; + let node = root.clone().lookup(&RelPath::new_canonicalized(".//foo/bar"))?; assert!(node.parent().is_some()); let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); + assert!(Arc::ptr_eq(&parent, &root.clone().lookup(&RelPath::new_canonicalized("foo"))?)); + assert!(parent.lookup(&RelPath::new_canonicalized("bar")).is_ok()); - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); + let node = root.clone().lookup(&RelPath::new_canonicalized("foo/.."))?; + assert!(Arc::ptr_eq(&node, &root.clone().lookup(&RelPath::new_canonicalized("."))?)); assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, + &root.clone().lookup(&RelPath::new_canonicalized("/foo/.."))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo/././bar/../.."))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, + &root.clone().lookup(&RelPath::new_canonicalized("././/foo//./../foo//bar///..//././"))?, + &devfs.root_dir().lookup(&RelPath::new_canonicalized(".//./foo/"))?, )); assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f3")?, - &root.lookup("foo/.//f3")?, + &root.clone().lookup(&RelPath::new_canonicalized("///foo//bar///../f3"))?, + &root.lookup(&RelPath::new_canonicalized("foo/.//f3"))?, )); Ok(()) @@ -109,16 +105,16 @@ fn test_ramfs() { let ramfs = RamFileSystem::new(); let root = ramfs.root_dir(); - root.create("f1", VfsNodeType::File).unwrap(); - root.create("f2", VfsNodeType::File).unwrap(); - root.create("foo", VfsNodeType::Dir).unwrap(); + root.create(&RelPath::new_canonicalized("f1"), VfsNodeType::File).unwrap(); + root.create(&RelPath::new_canonicalized("f2"), VfsNodeType::File).unwrap(); + root.create(&RelPath::new_canonicalized("foo"), VfsNodeType::Dir).unwrap(); - let dir_foo = root.lookup("foo").unwrap(); - dir_foo.create("f3", VfsNodeType::File).unwrap(); - dir_foo.create("bar", VfsNodeType::Dir).unwrap(); + let dir_foo = root.lookup(&RelPath::new_canonicalized("foo")).unwrap(); + dir_foo.create(&RelPath::new_canonicalized("f3"), VfsNodeType::File).unwrap(); + dir_foo.create(&RelPath::new_canonicalized("bar"), VfsNodeType::Dir).unwrap(); - let dir_bar = dir_foo.lookup("bar").unwrap(); - dir_bar.create("f4", VfsNodeType::File).unwrap(); + let dir_bar = dir_foo.lookup(&RelPath::new_canonicalized("bar")).unwrap(); + dir_bar.create(&RelPath::new_canonicalized("f4"), VfsNodeType::File).unwrap(); let mut entries = ramfs.root_dir_node().get_entries(); entries.sort(); @@ -128,18 +124,18 @@ fn test_ramfs() { test_get_parent(&ramfs).unwrap(); let root = ramfs.root_dir(); - assert_eq!(root.remove("f1"), Ok(())); - assert_eq!(root.remove("//f2"), Ok(())); - assert_eq!(root.remove("f3").err(), Some(VfsError::NotFound)); - assert_eq!(root.remove("foo").err(), Some(VfsError::DirectoryNotEmpty)); - assert_eq!(root.remove("foo/..").err(), Some(VfsError::InvalidInput)); + assert_eq!(root.remove(&RelPath::new_canonicalized("f1")), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("//f2")), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("f3")).err(), Some(VfsError::NotFound)); + assert_eq!(root.remove(&RelPath::new_canonicalized("foo")).err(), Some(VfsError::DirectoryNotEmpty)); + assert_eq!(root.remove(&RelPath::new_canonicalized("foo/..")).err(), Some(VfsError::InvalidInput)); assert_eq!( - root.remove("foo/./bar").err(), + root.remove(&RelPath::new_canonicalized("foo/./bar")).err(), Some(VfsError::DirectoryNotEmpty) ); - assert_eq!(root.remove("foo/bar/f4"), Ok(())); - assert_eq!(root.remove("foo/bar"), Ok(())); - assert_eq!(root.remove("./foo//.//f3"), Ok(())); - assert_eq!(root.remove("./foo"), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("foo/bar/f4")), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("foo/bar")), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("./foo//.//f3")), Ok(())); + assert_eq!(root.remove(&RelPath::new_canonicalized("./foo")), Ok(())); assert!(ramfs.root_dir_node().get_entries().is_empty()); } From fdd86e83a76a192cef5d4aba25759c0e255685e4 Mon Sep 17 00:00:00 2001 From: liujingx Date: Mon, 28 Oct 2024 12:32:44 +0800 Subject: [PATCH 08/36] fix: ruxfs api and posix api --- api/ruxos_posix_api/src/imp/fs.rs | 213 +++++++++++++++++--------- crates/axfs_vfs/src/path.rs | 18 ++- modules/ruxfs/src/api/dir.rs | 15 +- modules/ruxfs/src/api/file.rs | 19 ++- modules/ruxfs/src/api/mod.rs | 37 ++--- modules/ruxfs/src/fops.rs | 236 ++++++++++++++-------------- modules/ruxfs/src/fs/fatfs.rs | 30 ++-- modules/ruxfs/src/lib.rs | 19 ++- modules/ruxfs/src/mounts.rs | 48 +++--- modules/ruxfs/src/root.rs | 247 ++++++++++++++++-------------- 10 files changed, 488 insertions(+), 394 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 617c1fe72..075c72989 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -11,12 +11,13 @@ use alloc::{borrow::Cow, string::String, sync::Arc}; use core::ffi::{c_char, c_int, c_long, c_void, CStr}; use axerrno::{LinuxError, LinuxResult}; -use axio::{PollState, SeekFrom}; +use axio::{Error, PollState, SeekFrom}; use axsync::Mutex; use ruxfdtable::{FileLike, RuxStat}; use ruxfs::{ api::{current_dir, set_current_dir}, fops::{DirEntry, OpenOptions}, + AbsPath, RelPath, }; use super::fd_ops::get_file_like; @@ -180,7 +181,7 @@ fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { ctypes::O_WRONLY => options.write(true), _ => { options.read(true); - options.write(true); + options.write(true) } }; if flags & ctypes::O_APPEND != 0 { @@ -203,18 +204,18 @@ fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { /// Return its index in the file table (`fd`). Return `EMFILE` if it already /// has the maximum number of files open. pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let filename = char_ptr_to_absolute_path(filename); + let filename = char_ptr_to_path(filename); debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode); syscall_body!(sys_open, { let options = flags_to_options(flags, mode); - let file = ruxfs::fops::File::open(&filename?, &options)?; + let file = ruxfs::root::open_file(&filename?.absolute(), &options)?; File::new(file).add_to_fd_table() }) } /// Open a file under a specific dir pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let path = char_ptr_to_absolute_path(path); + let path = char_ptr_to_path(path); let fd: c_int = fd as c_int; debug!( "sys_openat <= {}, {:?}, {:#o}, {:#o}", @@ -223,32 +224,68 @@ pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mo syscall_body!(sys_openat, { let path = path?; let options = flags_to_options(flags, mode); - if fd == ctypes::AT_FDCWD || path.starts_with("/") { - let is_dir = ruxfs::fops::Directory::get_child_attr(&path)?.is_dir(); - // O_DIRECTORY flag is set but the path is not a directory, return ENOTDIR - if (flags as u32 & ctypes::O_DIRECTORY != 0) && !is_dir { - return Err(LinuxError::ENOTDIR); + let dflag = flags as u32 & ctypes::O_DIRECTORY != 0; + let cflag = flags as u32 & ctypes::O_CREAT != 0; + + let attr = match &path { + Path::Absolute(path) => { + ruxfs::root::get_attr(&path) } - if is_dir { - let dir = ruxfs::fops::Directory::open_dir(&path, &options)?; - Directory::new(dir).add_to_fd_table() - } else { - let file = ruxfs::fops::File::open(&path, &options)?; - File::new(file).add_to_fd_table() + Path::Relative(path) => { + if fd == ctypes::AT_FDCWD { + ruxfs::root::get_attr(¤t_dir()?.join(&path)) + } else { + let dir = Directory::from_fd(fd)?; + let attr = dir.inner.lock().get_child_attr_at(&path); + attr + } } - } else { - let dir = Directory::from_fd(fd)?; - let is_dir = dir.inner.lock().get_child_attr_at(&path)?.is_dir(); - // O_DIRECTORY flag is set but the path is not a directory, return ENOTDIR - if (flags as u32 & ctypes::O_DIRECTORY != 0) && !is_dir { - return Err(LinuxError::ENOTDIR); + }; + // Check child attributes first + let is_dir = match attr { + Ok(inner) => { + if !inner.is_dir() && dflag { + return Err(LinuxError::ENOTDIR); + } + inner.is_dir() } - if is_dir { - let dir = dir.inner.lock().open_dir_at(&path, &options)?; - Directory::new(dir).add_to_fd_table() - } else { - let file = dir.inner.lock().open_file_at(&path, &options)?; - File::new(file).add_to_fd_table() + Err(Error::NotFound) => { + if !cflag { + return Err(LinuxError::ENOENT); + } + dflag + } + Err(e) => return Err(e.into()), + }; + // Open file or directory + match path { + Path::Absolute(path) => { + if is_dir { + let dir = ruxfs::root::open_dir(&path, &options)?; + Directory::new(dir).add_to_fd_table() + } else { + let file = ruxfs::root::open_file(&path, &options)?; + File::new(file).add_to_fd_table() + } + } + Path::Relative(ref path) => { + if fd == ctypes::AT_FDCWD { + if is_dir { + let dir = ruxfs::root::open_dir(¤t_dir()?.join(&path), &options)?; + Directory::new(dir).add_to_fd_table() + } else { + let file = ruxfs::root::open_file(¤t_dir()?.join(&path), &options)?; + File::new(file).add_to_fd_table() + } + } else { + if is_dir { + let dir = Directory::from_fd(fd)?.inner.lock().open_dir_at(&path, &options)?; + Directory::new(dir).add_to_fd_table() + } else { + let file = Directory::from_fd(fd)?.inner.lock().open_file_at(&path, &options)?; + File::new(file).add_to_fd_table() + } + } } } }) @@ -331,7 +368,7 @@ pub unsafe fn sys_fdatasync(fd: c_int) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_int { - let path = char_ptr_to_absolute_path(path); + let path = char_ptr_to_path(path); debug!("sys_stat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_stat, { if buf.is_null() { @@ -339,7 +376,7 @@ pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_in } let mut options = OpenOptions::new(); options.read(true); - let file = ruxfs::fops::File::open(&path?, &options)?; + let file = ruxfs::root::open_file(&path?.absolute(), &options)?; let st: ctypes::stat = File::new(file).stat()?.into(); #[cfg(not(feature = "musl"))] @@ -412,7 +449,7 @@ pub fn sys_fstat(fd: c_int, kst: *mut core::ffi::c_void) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t { - let path = char_ptr_to_absolute_path(path); + let path = char_ptr_to_path(path); debug!("sys_lstat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_lstat, { if buf.is_null() { @@ -430,7 +467,7 @@ pub unsafe fn sys_newfstatat( kst: *mut ctypes::kstat, flag: c_int, ) -> c_int { - let path = char_ptr_to_absolute_path(path); + let path = char_ptr_to_path(path); debug!( "sys_newfstatat <= fd: {}, path: {:?}, flag: {:x}", _fd, path, flag @@ -441,7 +478,7 @@ pub unsafe fn sys_newfstatat( } let mut options = OpenOptions::new(); options.read(true); - let file = ruxfs::fops::File::open(&path?, &options)?; + let file = ruxfs::root::open_file(&path?.absolute(), &options)?; let st = File::new(file).stat()?; unsafe { (*kst).st_dev = st.st_dev; @@ -484,10 +521,10 @@ pub fn sys_getcwd(buf: *mut c_char, size: usize) -> c_int { /// Return 0 if the operation succeeds, otherwise return -1. pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int { syscall_body!(sys_rename, { - let old_path = char_ptr_to_absolute_path(old)?; - let new_path = char_ptr_to_absolute_path(new)?; + let old_path = char_ptr_to_path(old)?; + let new_path = char_ptr_to_path(new)?; debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path); - ruxfs::api::rename(&old_path, &new_path)?; + ruxfs::api::rename(&old_path.absolute(), &new_path.absolute())?; Ok(0) }) } @@ -496,8 +533,8 @@ pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int { /// /// TODO: only support `oldfd`, `newfd` equals to AT_FDCWD pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const c_char) -> c_int { - let old_path = char_ptr_to_absolute_path(old); - let new_path = char_ptr_to_absolute_path(new); + let old_path = char_ptr_to_path(old); + let new_path = char_ptr_to_path(new); debug!( "sys_renameat <= oldfd: {}, old: {:?}, newfd: {}, new: {:?}", oldfd, old_path, newfd, new_path @@ -505,7 +542,7 @@ pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const assert_eq!(oldfd, ctypes::AT_FDCWD as c_int); assert_eq!(newfd, ctypes::AT_FDCWD as c_int); syscall_body!(sys_renameat, { - ruxfs::api::rename(&old_path?, &new_path?)?; + ruxfs::api::rename(&old_path?.absolute(), &new_path?.absolute())?; Ok(0) }) } @@ -513,9 +550,9 @@ pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const /// Remove a directory, which must be empty pub fn sys_rmdir(pathname: *const c_char) -> c_int { syscall_body!(sys_rmdir, { - let path = char_ptr_to_absolute_path(pathname)?; + let path = char_ptr_to_path(pathname)?; debug!("sys_rmdir <= path: {:?}", path); - ruxfs::api::remove_dir(&path)?; + ruxfs::api::remove_dir(&path.absolute())?; Ok(0) }) } @@ -523,9 +560,9 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { /// Removes a file from the filesystem. pub fn sys_unlink(pathname: *const c_char) -> c_int { syscall_body!(sys_unlink, { - let path = char_ptr_to_absolute_path(pathname)?; + let path = char_ptr_to_path(pathname)?; debug!("sys_unlink <= path: {:?}", path); - ruxfs::api::remove_file(&path)?; + ruxfs::api::remove_file(&path.absolute())?; Ok(0) }) } @@ -535,7 +572,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { debug!( "sys_unlinkat <= fd: {}, pathname: {:?}, flags: {}", fd, - char_ptr_to_absolute_path(pathname), + char_ptr_to_path(pathname), flags ); if flags as u32 & ctypes::AT_REMOVEDIR != 0 { @@ -548,9 +585,16 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { // TODO: implement mode syscall_body!(sys_mkdir, { - let path = char_ptr_to_absolute_path(pathname)?; + let path = char_ptr_to_path(pathname)?; debug!("sys_mkdir <= path: {:?}, mode: {:?}", path, mode); - ruxfs::api::create_dir(&path)?; + match path { + Path::Absolute(p) => { + ruxfs::api::create_dir(&p)? + } + Path::Relative(p) => { + ruxfs::api::create_dir(¤t_dir()?.join(&p))? + } + } Ok(0) }) } @@ -562,7 +606,7 @@ pub fn sys_mkdirat(fd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> debug!( "sys_mkdirat <= fd: {}, pathname: {:?}, mode: {:x?}", fd, - char_ptr_to_absolute_path(pathname), + char_ptr_to_path(pathname), mode ); sys_mkdir(pathname, mode) @@ -579,7 +623,7 @@ pub fn sys_fchownat( debug!( "sys_fchownat <= fd: {}, path: {:?}, uid: {}, gid: {}, flag: {}", fd, - char_ptr_to_absolute_path(path), + char_ptr_to_path(path), uid, gid, flag @@ -595,7 +639,7 @@ pub fn sys_readlinkat( buf: *mut c_char, bufsize: usize, ) -> usize { - let path = char_ptr_to_absolute_path(pathname); + let path = char_ptr_to_path(pathname); debug!( "sys_readlinkat <= path = {:?}, fd = {:}, buf = {:p}, bufsize = {:}", path, fd, buf, bufsize @@ -717,57 +761,74 @@ pub unsafe fn sys_preadv( /// The mode is either the value F_OK, for the existence of the file, /// or a mask consisting of the bitwise OR of one or more of R_OK, W_OK, and X_OK, for the read, write, execute permissions. pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: c_int) -> c_int { - let path = char_ptr_to_absolute_path(pathname).unwrap(); + let path = char_ptr_to_path(pathname).unwrap(); debug!( "sys_faccessat <= dirfd {} path {} mode {} flags {}", dirfd, path, mode, flags ); syscall_body!(sys_faccessat, { - let mut options = OpenOptions::new(); - options.read(true); - let _file = ruxfs::fops::File::open(&path, &options)?; + // TODO: dirfd + // let mut options = OpenOptions::new(); + // options.read(true); + // let _file = options.open(path)?; Ok(0) }) } /// changes the current working directory to the directory specified in path. pub fn sys_chdir(path: *const c_char) -> c_int { - let p = char_ptr_to_absolute_path(path).unwrap(); + let p = char_ptr_to_path(path).unwrap(); debug!("sys_chdir <= path: {}", p); syscall_body!(sys_chdir, { - set_current_dir(&p)?; + match p { + Path::Absolute(p) => set_current_dir(p)?, + Path::Relative(p) => set_current_dir(current_dir()?.join(&p))?, + } Ok(0) }) } -/// from char_ptr get absolute_path_str -pub fn char_ptr_to_absolute_path<'a>(ptr: *const c_char) -> LinuxResult> { +/// Generic path type. +#[derive(Debug)] +enum Path<'a> { + Absolute(AbsPath<'a>), + Relative(RelPath<'a>), +} + +impl<'a> Path<'a> { + /// Transforms the path into a `AbsPath`. + /// + /// * If the path is already an absolute path, it is returned as is. + /// * If the path is a relative path, it is resolved against the current working directory. + pub fn absolute(self) -> AbsPath<'a> { + match self { + Path::Absolute(p) => p, + Path::Relative(p) => current_dir().unwrap().join(&p), + } + } +} + +impl core::fmt::Display for Path<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Path::Absolute(p) => write!(f, "{}", p), + Path::Relative(p) => write!(f, "{}", p), + } + } +} + +/// from char_ptr get path_str +pub fn char_ptr_to_path<'a>(ptr: *const c_char) -> LinuxResult> { if ptr.is_null() { return Err(LinuxError::EFAULT); } - let path = unsafe { let cstr = CStr::from_ptr(ptr); cstr.to_str().map_err(|_| LinuxError::EINVAL)? }; - - if path.starts_with("..") { - let stripped = path.strip_prefix("..").unwrap(); - let mut cwd = ruxfs::api::current_dir()?; - if let Some(index) = cwd.rfind('/') { - cwd.truncate(index); - if let Some(index) = cwd.rfind('/') { - cwd.truncate(index); - } - } - let absolute_path: String = cwd + stripped; - Ok(Cow::Owned(absolute_path)) - } else if path.starts_with('.') { - let stripped = path.strip_prefix('.').unwrap(); - let cwd = ruxfs::api::current_dir()?; - let absolute_path: String = cwd + stripped; - Ok(Cow::Owned(absolute_path)) + if path.starts_with('/') { + Ok(Path::Absolute(AbsPath::new_canonicalized(path))) } else { - Ok(Cow::Borrowed(path)) + Ok(Path::Relative(RelPath::new_canonicalized(path))) } } diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index 894f52a7c..ce63d89da 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -10,8 +10,7 @@ //! Utilities for path manipulation. use alloc::{ - borrow::{Cow, ToOwned}, - string::String, + borrow::{Cow, ToOwned}, format, string::String }; /// Returns the canonical form of the path with all intermediate components @@ -66,12 +65,12 @@ pub fn canonicalize(path: &str) -> String { /// CANONICALIZED absolute path type, starting with '/'. /// /// Using `Cow` type to avoid unnecessary allocations. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct AbsPath<'a>(Cow<'a, str>); impl<'a> AbsPath<'a> { /// Simply wrap a string into a `AbsPath`. - pub fn new(path: &'a str) -> Self { + pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } @@ -83,6 +82,16 @@ impl<'a> AbsPath<'a> { Self(Cow::Owned(canonicalize(path))) } } + + /// Transform into a `RelPath`. + pub fn to_rel(&self) -> RelPath { + RelPath(Cow::Borrowed(self.0.trim_start_matches('/'))) + } + + /// Concatenate a relative path to this absolute path. + pub fn join(&self, rel: &RelPath) -> Self { + Self::new_canonicalized(&format!("{}/{}", self.0, rel.0)) + } } impl core::ops::Deref for AbsPath<'_> { @@ -110,6 +119,7 @@ impl core::fmt::Display for AbsPath<'_> { /// - "a/b/c" /// /// Using `Cow` type to avoid unnecessary allocations. +#[derive(Debug, Clone)] pub struct RelPath<'a>(Cow<'a, str>); impl<'a> RelPath<'a> { diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index 4f10aaca6..0c1d0f259 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -8,6 +8,7 @@ */ use alloc::string::String; +use axfs_vfs::path::AbsPath; use axio::Result; use core::fmt; @@ -16,7 +17,7 @@ use crate::fops; /// Iterator over the entries in a directory. pub struct ReadDir<'a> { - path: &'a str, + path: &'a AbsPath<'a>, inner: fops::Directory, buf_pos: usize, buf_end: usize, @@ -38,10 +39,10 @@ pub struct DirBuilder { } impl<'a> ReadDir<'a> { - pub(super) fn new(path: &'a str) -> Result { + pub(super) fn new(path: &'a AbsPath<'a>) -> Result { let mut opts = fops::OpenOptions::new(); opts.read(true); - let inner = fops::Directory::open_dir(path, &opts)?; + let inner = crate::root::open_dir(path, &opts)?; const EMPTY: fops::DirEntry = fops::DirEntry::default(); let dirent_buf = [EMPTY; 31]; Ok(ReadDir { @@ -142,17 +143,17 @@ impl DirBuilder { /// Creates the specified directory with the options configured in this /// builder. - pub fn create(&self, path: &str) -> Result<()> { + pub fn create(&self, path: &AbsPath) -> Result<()> { if self.recursive { self.create_dir_all(path) } else { - crate::root::create_dir(None, path) + crate::root::create_dir(path) } } /// Recursively create a directory and all of its parent components if they /// are missing. - pub fn create_dir_all(&self, path: &str) -> Result<()> { - crate::root::create_dir_all(None, path) + pub fn create_dir_all(&self, path: &AbsPath) -> Result<()> { + crate::root::create_dir_all(path) } } diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index ea4eecdf0..82186ef41 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -7,10 +7,10 @@ * See the Mulan PSL v2 for more details. */ +use axfs_vfs::path::AbsPath; use axio::{prelude::*, Result, SeekFrom}; use core::fmt; - -use crate::fops; +use crate::fops::{self, Directory}; /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. @@ -74,8 +74,13 @@ impl OpenOptions { } /// Opens a file at `path` with the options specified by `self`. - pub fn open(&self, path: &str) -> Result { - fops::File::open(path, &self.0).map(|inner| File { inner }) + pub fn open(&self, path: &AbsPath) -> Result { + crate::root::open_file(&path, &self.0).map(|inner| File { inner }) + } + + /// Opens a directory at `path` with the options specified by `self`. + pub fn open_dir(&self, path: &AbsPath) -> Result { + crate::root::open_dir(&path, &self.0) } } @@ -134,12 +139,12 @@ impl fmt::Debug for Metadata { impl File { /// Attempts to open a file in read-only mode. - pub fn open(path: &str) -> Result { + pub fn open(path: &AbsPath) -> Result { OpenOptions::new().read(true).open(path) } /// Opens a file in write-only mode. - pub fn create(path: &str) -> Result { + pub fn create(path: &AbsPath) -> Result { OpenOptions::new() .write(true) .create(true) @@ -148,7 +153,7 @@ impl File { } /// Creates a new file in read-write mode; error if the file exists. - pub fn create_new(path: &str) -> Result { + pub fn create_new(path: &AbsPath) -> Result { OpenOptions::new() .read(true) .write(true) diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index f81c375cf..07a44d477 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -16,31 +16,26 @@ pub use self::dir::{DirBuilder, DirEntry, ReadDir}; pub use self::file::{File, FileType, Metadata, OpenOptions, Permissions}; use alloc::{string::String, vec::Vec}; +use axfs_vfs::path::AbsPath; use axio::{self as io, prelude::*}; /// Returns an iterator over the entries within a directory. -pub fn read_dir(path: &str) -> io::Result { +pub fn read_dir<'a>(path: &'a AbsPath<'a>) -> io::Result> { ReadDir::new(path) } -/// Returns the canonical, absolute form of a path with all intermediate -/// components normalized. -pub fn canonicalize(path: &str) -> io::Result { - crate::root::absolute_path(path) -} - -/// Returns the current working directory as a [`String`]. -pub fn current_dir() -> io::Result { +/// Returns the current working directory as a [`AbsPath`]. +pub fn current_dir() -> io::Result> { crate::root::current_dir() } /// Changes the current working directory to the specified path. -pub fn set_current_dir(path: &str) -> io::Result<()> { +pub fn set_current_dir(path: AbsPath<'static>) -> io::Result<()> { crate::root::set_current_dir(path) } /// Read the entire contents of a file into a bytes vector. -pub fn read(path: &str) -> io::Result> { +pub fn read(path: &AbsPath) -> io::Result> { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len()).unwrap_or(0); let mut bytes = Vec::with_capacity(size as usize); @@ -49,7 +44,7 @@ pub fn read(path: &str) -> io::Result> { } /// Read the entire contents of a file into a string. -pub fn read_to_string(path: &str) -> io::Result { +pub fn read_to_string(path: &AbsPath) -> io::Result { let mut file = File::open(path)?; let size = file.metadata().map(|m| m.len()).unwrap_or(0); let mut string = String::with_capacity(size as usize); @@ -58,41 +53,41 @@ pub fn read_to_string(path: &str) -> io::Result { } /// Write a slice as the entire contents of a file. -pub fn write>(path: &str, contents: C) -> io::Result<()> { +pub fn write>(path: &AbsPath, contents: C) -> io::Result<()> { File::create(path)?.write_all(contents.as_ref()) } /// Given a path, query the file system to get information about a file, /// directory, etc. -pub fn metadata(path: &str) -> io::Result { +pub fn metadata(path: &AbsPath) -> io::Result { File::open(path)?.metadata() } /// Creates a new, empty directory at the provided path. -pub fn create_dir(path: &str) -> io::Result<()> { +pub fn create_dir(path: &AbsPath) -> io::Result<()> { DirBuilder::new().create(path) } /// Recursively create a directory and all of its parent components if they /// are missing. -pub fn create_dir_all(path: &str) -> io::Result<()> { +pub fn create_dir_all(path: &AbsPath) -> io::Result<()> { DirBuilder::new().recursive(true).create(path) } /// Removes an empty directory. -pub fn remove_dir(path: &str) -> io::Result<()> { - crate::root::remove_dir(None, path) +pub fn remove_dir(path: &AbsPath) -> io::Result<()> { + crate::root::remove_dir(path) } /// Removes a file from the filesystem. -pub fn remove_file(path: &str) -> io::Result<()> { - crate::root::remove_file(None, path) +pub fn remove_file(path: &AbsPath) -> io::Result<()> { + crate::root::remove_file(path) } /// Rename a file or directory to a new name. /// Delete the original file if `old` already exists. /// /// This only works then the new path is in the same mounted fs. -pub fn rename(old: &str, new: &str) -> io::Result<()> { +pub fn rename(old: &AbsPath, new: &AbsPath) -> io::Result<()> { crate::root::rename(old, new) } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 3ffdf1452..27c837ecb 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -9,11 +9,12 @@ //! Low-level filesystem operations. -use axerrno::{ax_err, ax_err_type, AxResult}; -use axfs_vfs::{VfsError, VfsNodeAttr, VfsNodeRef}; +use axerrno::{ax_err, ax_err_type, AxResult, AxError}; +use axfs_vfs::path::RelPath; +use axfs_vfs::{VfsError, VfsNodeRef, VfsNodeType}; use axio::SeekFrom; use capability::{Cap, WithCap}; -use core::{fmt, ops}; +use core::fmt; #[cfg(feature = "myfs")] pub use crate::dev::Disk; @@ -47,12 +48,12 @@ pub struct Directory { #[derive(Clone)] pub struct OpenOptions { // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, + pub read: bool, + pub write: bool, + pub append: bool, + pub truncate: bool, + pub create: bool, + pub create_new: bool, // system-specific _custom_flags: i32, _mode: u32, @@ -99,7 +100,7 @@ impl OpenOptions { self.create_new = create_new; } - const fn is_valid(&self) -> bool { + pub const fn is_valid(&self) -> bool { if !self.read && !self.write && !self.append { return false; } @@ -121,57 +122,13 @@ impl OpenOptions { } impl File { - fn _open_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult { - debug!("open file: {} {:?}", path, opts); - if !opts.is_valid() { - return ax_err!(InvalidInput); - } - - let node_option = crate::root::lookup(dir, path); - let node = if opts.create || opts.create_new { - match node_option { - Ok(node) => { - // already exists - if opts.create_new { - return ax_err!(AlreadyExists); - } - node - } - // not exists, create new - Err(VfsError::NotFound) => crate::root::create_file(dir, path)?, - Err(e) => return Err(e), - } - } else { - // just open the existing - node_option? - }; - - let attr = node.get_attr()?; - if attr.is_dir() - && (opts.create || opts.create_new || opts.write || opts.append || opts.truncate) - { - return ax_err!(IsADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { - return ax_err!(PermissionDenied); - } - - node.open()?; - if opts.truncate { - node.truncate(0)?; - } - Ok(Self { - node: WithCap::new(node, access_cap), - is_append: opts.append, + /// Create an opened file. + pub fn new(node: VfsNodeRef, cap: Cap, is_append: bool) -> Self { + Self { + node: WithCap::new(node, cap), offset: 0, - }) - } - - /// Opens a file at the path relative to the current directory. Returns a - /// [`File`] object. - pub fn open(path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_at(None, path, opts) + is_append, + } } /// Truncates the file to the specified size. @@ -252,7 +209,28 @@ impl File { } impl Directory { - fn _open_dir_at(dir: Option<&VfsNodeRef>, path: &str, opts: &OpenOptions) -> AxResult { + /// Access the underlying `VfsNode` + fn access_node(&self) -> AxResult<&VfsNodeRef> { + self.node.access(Cap::EXECUTE).or(ax_err!(PermissionDenied)) + } + + /// Creates an opened directory. + pub fn new(node: VfsNodeRef, cap: Cap) -> Self { + Self { + node: WithCap::new(node, cap), + entry_idx: 0, + } + } + + /// Gets the file attributes of the file at the path relative to this directory. + /// Returns a [`FileAttr`] object. + pub fn get_child_attr_at(&self, path: &RelPath) -> AxResult { + self.access_node()?.clone().lookup(path)?.get_attr() + } + + /// Opens a directory at the path relative to this directory. Returns a + /// [`Directory`] object. + pub fn open_dir_at(&self, path: &RelPath, opts: &OpenOptions) -> AxResult { debug!("open dir: {}", path); if !opts.read { return ax_err!(InvalidInput); @@ -260,8 +238,7 @@ impl Directory { if opts.create || opts.create_new || opts.write || opts.append || opts.truncate { return ax_err!(InvalidInput); } - - let node = crate::root::lookup(dir, path)?; + let node = self.access_node()?.clone().lookup(path)?; let attr = node.get_attr()?; if !attr.is_dir() { return ax_err!(NotADirectory); @@ -270,75 +247,95 @@ impl Directory { if !perm_to_cap(attr.perm()).contains(access_cap) { return ax_err!(PermissionDenied); } - node.open()?; - Ok(Self { - node: WithCap::new(node, access_cap | Cap::EXECUTE), - entry_idx: 0, - }) - } - - fn _get_child_attr_at(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - let node = crate::root::lookup(dir, path)?; - Ok(node.get_attr()?) + Ok(Self::new(node, access_cap | Cap::EXECUTE)) } - fn access_at(&self, path: &str) -> AxResult> { - if path.starts_with('/') { - Ok(None) - } else { - Ok(Some(self.node.access(Cap::EXECUTE)?)) + /// Opens a file at the path relative to this directory. Returns a [`File`] + /// object. + pub fn open_file_at(&self, path: &RelPath, opts: &OpenOptions) -> AxResult { + debug!("open file: {} {:?}", path, opts); + if !opts.is_valid() { + return ax_err!(InvalidInput); } - } - - /// Gets the file attributes of the file at the path relative to current directory. - /// Returns a [`FileAttr`] object. - pub fn get_child_attr(path: &str) -> AxResult { - Self::_get_child_attr_at(None, path) - } - - /// Gets the file attributes of the file at the path relative to this directory. - /// Returns a [`FileAttr`] object. - pub fn get_child_attr_at(&self, path: &str) -> AxResult { - Self::_get_child_attr_at(self.access_at(path)?, path) - } - - /// Opens a directory at the path relative to the current directory. - /// Returns a [`Directory`] object. - pub fn open_dir(path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_dir_at(None, path, opts) - } + let node = match self.access_node()?.clone().lookup(path) { + Ok(node) => { + if opts.create_new { + return ax_err!(AlreadyExists); + } + node + } + Err(VfsError::NotFound) => { + if !opts.create || !opts.create_new { + return ax_err!(NotFound); + } + self.access_node()?.clone().create(path, VfsNodeType::File)?; + self.access_node()?.clone().lookup(path)? + } + Err(e) => return Err(e), + }; - /// Opens a directory at the path relative to this directory. Returns a - /// [`Directory`] object. - pub fn open_dir_at(&self, path: &str, opts: &OpenOptions) -> AxResult { - Self::_open_dir_at(self.access_at(path)?, path, opts) - } + let attr = node.get_attr()?; + if attr.is_dir() { + return ax_err!(IsADirectory); + } + let access_cap = opts.into(); + if !perm_to_cap(attr.perm()).contains(access_cap) { + return ax_err!(PermissionDenied); + } - /// Opens a file at the path relative to this directory. Returns a [`File`] - /// object. - pub fn open_file_at(&self, path: &str, opts: &OpenOptions) -> AxResult { - File::_open_at(self.access_at(path)?, path, opts) + node.open()?; + if opts.truncate { + node.truncate(0)?; + } + Ok(File::new(node, access_cap, opts.append)) } /// Creates an empty file at the path relative to this directory. - pub fn create_file(&self, path: &str) -> AxResult { - crate::root::create_file(self.access_at(path)?, path) + pub fn create_file(&self, path: &RelPath) -> AxResult { + match self.access_node()?.clone().lookup(path) { + Ok(_) => ax_err!(AlreadyExists), + Err(AxError::NotFound) => { + self.access_node()?.clone().create(path, VfsNodeType::File)?; + self.access_node()?.clone().lookup(path) + } + Err(e) => Err(e), + } } /// Creates an empty directory at the path relative to this directory. - pub fn create_dir(&self, path: &str) -> AxResult { - crate::root::create_dir(self.access_at(path)?, path) + pub fn create_dir(&self, path: &RelPath) -> AxResult { + match self.access_node()?.clone().lookup(path) { + Ok(_) => ax_err!(AlreadyExists), + Err(AxError::NotFound) => self.access_node()?.create(path, VfsNodeType::Dir), + Err(e) => Err(e), + } } /// Removes a file at the path relative to this directory. - pub fn remove_file(&self, path: &str) -> AxResult { - crate::root::remove_file(self.access_at(path)?, path) + pub fn remove_file(&self, path: &RelPath) -> AxResult { + let node = self.access_node()?.clone().lookup(path)?; + let attr = node.get_attr()?; + if attr.is_dir() { + ax_err!(IsADirectory) + } else if !attr.perm().owner_writable() { + ax_err!(PermissionDenied) + } else { + self.access_node()?.remove(path) + } } /// Removes a directory at the path relative to this directory. - pub fn remove_dir(&self, path: &str) -> AxResult { - crate::root::remove_dir(self.access_at(path)?, path) + pub fn remove_dir(&self, path: &RelPath) -> AxResult { + let node = self.access_node()?.clone().lookup(path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + ax_err!(NotADirectory) + } else if !attr.perm().owner_writable() { + ax_err!(PermissionDenied) + } else { + self.access_node()?.remove(path) + } } /// Reads directory entries starts from the current position into the @@ -369,8 +366,13 @@ impl Directory { /// Delete the original file if `old` already exists. /// /// This only works then the new path is in the same mounted fs. - pub fn rename(&self, old: &str, new: &str) -> AxResult { - crate::root::rename(old, new) + pub fn rename(&self, old: &RelPath, new: &RelPath) -> AxResult { + if self.access_node()?.clone().lookup(new).is_ok() { + warn!("dst file already exist, now remove it"); + ax_err!(AlreadyExists) + } else { + self.access_node()?.rename(old, new) + } } /// Gets the file attributes. @@ -429,7 +431,7 @@ impl From<&OpenOptions> for Cap { } } -fn perm_to_cap(perm: FilePerm) -> Cap { +pub fn perm_to_cap(perm: FilePerm) -> Cap { let mut cap = Cap::empty(); if perm.owner_readable() { cap |= Cap::READ; diff --git a/modules/ruxfs/src/fs/fatfs.rs b/modules/ruxfs/src/fs/fatfs.rs index 3b27b7f6f..047cbc459 100644 --- a/modules/ruxfs/src/fs/fatfs.rs +++ b/modules/ruxfs/src/fs/fatfs.rs @@ -8,6 +8,8 @@ */ use alloc::sync::Arc; +use axerrno::ax_err; +use axfs_vfs::path::RelPath; use core::cell::UnsafeCell; use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; @@ -144,16 +146,11 @@ impl VfsNodeOps for DirWrapper<'static> { .map_or(None, |dir| Some(FatFileSystem::new_dir(dir))) } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { debug!("lookup at fatfs: {}", path); - let path = path.trim_matches('/'); - if path.is_empty() || path == "." { + if path.is_empty() { return Ok(self.clone()); } - if let Some(rest) = path.strip_prefix("./") { - return self.lookup(rest); - } - if let Ok(Some(is_dir)) = self.0.check_path_type(path) { if is_dir { if let Ok(dir) = self.0.open_dir(path) { @@ -173,16 +170,11 @@ impl VfsNodeOps for DirWrapper<'static> { } } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { debug!("create {:?} at fatfs: {}", ty, path); - let path = path.trim_matches('/'); - if path.is_empty() || path == "." { + if path.is_empty() { return Ok(()); } - if let Some(rest) = path.strip_prefix("./") { - return self.create(rest, ty); - } - match ty { VfsNodeType::File => { self.0.create_file(path).map_err(as_vfs_err)?; @@ -196,12 +188,10 @@ impl VfsNodeOps for DirWrapper<'static> { } } - fn remove(&self, path: &str) -> VfsResult { + fn remove(&self, path: &RelPath) -> VfsResult { debug!("remove at fatfs: {}", path); - let path = path.trim_matches('/'); - assert!(!path.is_empty()); // already check at `root.rs` - if let Some(rest) = path.strip_prefix("./") { - return self.remove(rest); + if path.is_empty() { + return ax_err!(PermissionDenied) } self.0.remove(path).map_err(as_vfs_err) } @@ -227,7 +217,7 @@ impl VfsNodeOps for DirWrapper<'static> { Ok(dirents.len()) } - fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult { + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { // `src_path` and `dst_path` should in the same mounted fs debug!( "rename at fatfs, src_path: {}, dst_path: {}", diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index 6db931721..2a72442e5 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -38,13 +38,16 @@ extern crate alloc; mod dev; mod fs; mod mounts; -mod root; #[cfg(feature = "alloc")] mod arch; pub mod api; pub mod fops; +pub mod root; + +pub type AbsPath<'a> = axfs_vfs::path::AbsPath<'a>; +pub type RelPath<'a> = axfs_vfs::path::RelPath<'a>; use alloc::vec::Vec; @@ -63,7 +66,7 @@ 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()) + MountPoint::new(AbsPath::new("/"), mounts::ramfs()) } /// Initializes filesystems by block devices. @@ -85,32 +88,32 @@ pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint } } - MountPoint::new("/", blk_fs) + MountPoint::new(AbsPath::new("/"), blk_fs) } /// Initializes common filesystems. pub fn prepare_commonfs(mount_points: &mut Vec) { #[cfg(feature = "devfs")] - let mount_point = MountPoint::new("/dev", mounts::devfs()); + let mount_point = MountPoint::new(AbsPath::new("/dev"), mounts::devfs()); mount_points.push(mount_point); #[cfg(feature = "ramfs")] - let mount_point = MountPoint::new("/tmp", mounts::ramfs()); + let mount_point = MountPoint::new(AbsPath::new("/tmp"), mounts::ramfs()); mount_points.push(mount_point); // Mount another ramfs as procfs #[cfg(feature = "procfs")] - let mount_point = MountPoint::new("/proc", mounts::procfs().unwrap()); + let mount_point = MountPoint::new(AbsPath::new("/proc"), mounts::procfs().unwrap()); mount_points.push(mount_point); // Mount another ramfs as sysfs #[cfg(feature = "sysfs")] - let mount_point = MountPoint::new("/sys", mounts::sysfs().unwrap()); + let mount_point = MountPoint::new(AbsPath::new("/sys"), mounts::sysfs().unwrap()); mount_points.push(mount_point); // Mount another ramfs as etcfs #[cfg(feature = "etcfs")] - let mount_point = MountPoint::new("/etc", mounts::etcfs().unwrap()); + let mount_point = MountPoint::new(AbsPath::new("/etc"), mounts::etcfs().unwrap()); mount_points.push(mount_point); } diff --git a/modules/ruxfs/src/mounts.rs b/modules/ruxfs/src/mounts.rs index b5d74cae0..f3d08ebdc 100644 --- a/modules/ruxfs/src/mounts.rs +++ b/modules/ruxfs/src/mounts.rs @@ -8,7 +8,7 @@ */ use alloc::sync::Arc; -use axfs_vfs::{VfsNodeType, VfsOps, VfsResult}; +use axfs_vfs::{VfsNodeType, VfsOps, VfsResult, path::RelPath}; #[cfg(feature = "alloc")] use crate::arch::{get_cpuinfo, get_meminfo}; @@ -41,28 +41,28 @@ pub(crate) fn procfs() -> VfsResult> { #[cfg(feature = "alloc")] { // Create /proc/cpuinfo - proc_root.create("cpuinfo", VfsNodeType::File)?; - let file_cpuinfo = proc_root.clone().lookup("./cpuinfo")?; + proc_root.create(&RelPath::new("cpuinfo"), VfsNodeType::File)?; + let file_cpuinfo = proc_root.clone().lookup(&RelPath::new("cpuinfo"))?; file_cpuinfo.write_at(0, get_cpuinfo().as_bytes())?; // Create /proc/meminfo - proc_root.create("meminfo", VfsNodeType::File)?; - let file_meminfo = proc_root.clone().lookup("./meminfo")?; + proc_root.create(&RelPath::new("meminfo"), VfsNodeType::File)?; + let file_meminfo = proc_root.clone().lookup(&RelPath::new("meminfo"))?; file_meminfo.write_at(0, get_meminfo().as_bytes())?; } // Create /proc/sys/net/core/somaxconn - proc_root.create_recursive("sys/net/core/somaxconn", VfsNodeType::File)?; - let file_somaxconn = proc_root.clone().lookup("./sys/net/core/somaxconn")?; + proc_root.create_recursive(&RelPath::new("sys/net/core/somaxconn"), VfsNodeType::File)?; + let file_somaxconn = proc_root.clone().lookup(&RelPath::new("sys/net/core/somaxconn"))?; file_somaxconn.write_at(0, b"4096\n")?; // Create /proc/sys/vm/overcommit_memory - proc_root.create_recursive("sys/vm/overcommit_memory", VfsNodeType::File)?; - let file_over = proc_root.clone().lookup("./sys/vm/overcommit_memory")?; + proc_root.create_recursive(&RelPath::new("sys/vm/overcommit_memory"), VfsNodeType::File)?; + let file_over = proc_root.clone().lookup(&RelPath::new("sys/vm/overcommit_memory"))?; file_over.write_at(0, b"0\n")?; // Create /proc/self/stat - proc_root.create_recursive("self/stat", VfsNodeType::File)?; + proc_root.create_recursive(&RelPath::new("self/stat"), VfsNodeType::File)?; Ok(Arc::new(procfs)) } @@ -72,21 +72,23 @@ pub(crate) fn sysfs() -> VfsResult> { let sysfs = fs::ramfs::RamFileSystem::new(); let sys_root = sysfs.root_dir(); + debug!("sysfs: {:?}", sys_root.get_attr()); + // Create /sys/kernel/mm/transparent_hugepage/enabled - sys_root.create_recursive("kernel/mm/transparent_hugepage/enabled", VfsNodeType::File)?; + sys_root.create_recursive(&RelPath::new("kernel/mm/transparent_hugepage/enabled"), VfsNodeType::File)?; let file_hp = sys_root .clone() - .lookup("./kernel/mm/transparent_hugepage/enabled")?; + .lookup(&RelPath::new("kernel/mm/transparent_hugepage/enabled"))?; file_hp.write_at(0, b"always [madvise] never\n")?; // Create /sys/devices/system/clocksource/clocksource0/current_clocksource sys_root.create_recursive( - "devices/system/clocksource/clocksource0/current_clocksource", + &RelPath::new("devices/system/clocksource/clocksource0/current_clocksource"), VfsNodeType::File, )?; let file_cc = sys_root .clone() - .lookup("devices/system/clocksource/clocksource0/current_clocksource")?; + .lookup(&RelPath::new("devices/system/clocksource/clocksource0/current_clocksource"))?; file_cc.write_at(0, b"tsc\n")?; Ok(Arc::new(sysfs)) @@ -98,22 +100,22 @@ pub(crate) fn etcfs() -> VfsResult> { let etc_root = etcfs.root_dir(); // Create /etc/passwd, and /etc/hosts - etc_root.create("passwd", VfsNodeType::File)?; - let file_passwd = etc_root.clone().lookup("passwd")?; + etc_root.create(&RelPath::new("passwd"), VfsNodeType::File)?; + let file_passwd = etc_root.clone().lookup(&RelPath::new("passwd"))?; // format: username:password:uid:gid:allname:homedir:shell file_passwd.write_at(0, b"root:x:0:0:root:/root:/bin/bash\n")?; // Create /etc/group - etc_root.create("group", VfsNodeType::File)?; - let file_group = etc_root.clone().lookup("group")?; + etc_root.create(&RelPath::new("group"), VfsNodeType::File)?; + let file_group = etc_root.clone().lookup(&RelPath::new("group"))?; file_group.write_at(0, b"root:x:0:\n")?; // Create /etc/localtime - etc_root.create("localtime", VfsNodeType::File)?; + etc_root.create(&RelPath::new("localtime"), VfsNodeType::File)?; // Create /etc/hosts - etc_root.create("hosts", VfsNodeType::File)?; - let file_hosts = etc_root.clone().lookup("hosts")?; + etc_root.create(&RelPath::new("hosts"), VfsNodeType::File)?; + let file_hosts = etc_root.clone().lookup(&RelPath::new("hosts"))?; file_hosts.write_at( 0, b"127.0.0.1 localhost\n\n\ @@ -126,8 +128,8 @@ pub(crate) fn etcfs() -> VfsResult> { )?; // Create /etc/resolv.conf - etc_root.create("resolv.conf", VfsNodeType::File)?; - let file_resolv = etc_root.clone().lookup("resolv.conf")?; + etc_root.create(&RelPath::new("resolv.conf"), VfsNodeType::File)?; + let file_resolv = etc_root.clone().lookup(&RelPath::new("resolv.conf"))?; file_resolv.write_at( 0, b"nameserver 127.0.0.53\n\ diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index eb19f2c60..d73fcf804 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -11,20 +11,27 @@ //! //! TODO: it doesn't work very well if the mount points have containment relationships. -use alloc::{format, string::String, sync::Arc, vec::Vec}; +use alloc::{format, sync::Arc, vec::Vec}; use axerrno::{ax_err, AxError, AxResult}; -use axfs_vfs::{VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult}; +use axfs_vfs::{ + path::{AbsPath, RelPath}, + VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult, +}; use axsync::Mutex; +use capability::Cap; use lazy_init::LazyInit; -use crate::api::FileType; +use crate::{ + api::FileType, + fops::{perm_to_cap, Directory, File, OpenOptions}, +}; -static CURRENT_DIR_PATH: Mutex = Mutex::new(String::new()); +static CURRENT_DIR_PATH: Mutex = Mutex::new(AbsPath::new("/")); static CURRENT_DIR: LazyInit> = LazyInit::new(); /// mount point information pub struct MountPoint { - path: &'static str, + path: AbsPath<'static>, fs: Arc, } @@ -37,7 +44,7 @@ static ROOT_DIR: LazyInit> = LazyInit::new(); impl MountPoint { /// create new MountPoint from data - pub fn new(path: &'static str, fs: Arc) -> Self { + pub fn new(path: AbsPath<'static>, fs: Arc) -> Self { Self { path, fs } } } @@ -56,48 +63,42 @@ impl RootDirectory { } } - pub fn mount(&mut self, path: &'static str, fs: Arc) -> AxResult { - if path == "/" { + pub fn mount(&mut self, path: AbsPath<'static>, fs: Arc) -> AxResult { + if path == AbsPath::new("/") { return ax_err!(InvalidInput, "cannot mount root filesystem"); } - if !path.starts_with('/') { - return ax_err!(InvalidInput, "mount path must start with '/'"); - } if self.mounts.iter().any(|mp| mp.path == path) { return ax_err!(InvalidInput, "mount point already exists"); } // create the mount point in the main filesystem if it does not exist - match self.main_fs.root_dir().lookup(path) { + match self.main_fs.root_dir().lookup(&path.to_rel()) { Ok(_) => {} Err(err_code) => { if err_code == VfsError::NotFound { - self.main_fs.root_dir().create(path, FileType::Dir)?; + self.main_fs + .root_dir() + .create(&path.to_rel(), FileType::Dir)?; } } } - fs.mount(path, self.main_fs.root_dir().lookup(path)?)?; + fs.mount(&path, self.main_fs.root_dir().lookup(&path.to_rel())?)?; self.mounts.push(MountPoint::new(path, fs)); Ok(()) } - pub fn _umount(&mut self, path: &str) { - self.mounts.retain(|mp| mp.path != path); + pub fn _umount(&mut self, path: &AbsPath) { + self.mounts.retain(|mp| mp.path != *path); } - pub fn contains(&self, path: &str) -> bool { - self.mounts.iter().any(|mp| mp.path == path) + pub fn contains(&self, path: &AbsPath) -> bool { + self.mounts.iter().any(|mp| mp.path == *path) } - fn lookup_mounted_fs(&self, path: &str, f: F) -> AxResult + fn lookup_mounted_fs(&self, path: &RelPath, f: F) -> AxResult where - F: FnOnce(Arc, &str) -> AxResult, + F: FnOnce(Arc, &RelPath) -> AxResult, { debug!("lookup at root: {}", path); - let path = path.trim_matches('/'); - if let Some(rest) = path.strip_prefix("./") { - return self.lookup_mounted_fs(rest, f); - } - let mut idx = 0; let mut max_len = 0; @@ -114,7 +115,7 @@ impl RootDirectory { if max_len == 0 { f(self.main_fs.clone(), path) // not matched any mount point } else { - f(self.mounts[idx].fs.clone(), &path[max_len..]) // matched at `idx` + f(self.mounts[idx].fs.clone(), &RelPath::new(&path[max_len..])) // matched at `idx` } } } @@ -126,11 +127,11 @@ impl VfsNodeOps for RootDirectory { self.main_fs.root_dir().get_attr() } - fn lookup(self: Arc, path: &str) -> VfsResult { + fn lookup(self: Arc, path: &RelPath) -> VfsResult { self.lookup_mounted_fs(path, |fs, rest_path| fs.root_dir().lookup(rest_path)) } - fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { self.lookup_mounted_fs(path, |fs, rest_path| { if rest_path.is_empty() { Ok(()) // already exists @@ -140,7 +141,7 @@ impl VfsNodeOps for RootDirectory { }) } - fn remove(&self, path: &str) -> VfsResult { + fn remove(&self, path: &RelPath) -> VfsResult { self.lookup_mounted_fs(path, |fs, rest_path| { if rest_path.is_empty() { ax_err!(PermissionDenied) // cannot remove mount points @@ -150,7 +151,7 @@ impl VfsNodeOps for RootDirectory { }) } - fn rename(&self, src_path: &str, dst_path: &str) -> VfsResult { + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { self.lookup_mounted_fs(src_path, |fs, rest_path| { if rest_path.is_empty() { ax_err!(PermissionDenied) // cannot rename mount points @@ -170,133 +171,156 @@ pub(crate) fn init_rootfs(mount_points: Vec) { let mut root_dir = RootDirectory::new(main_fs); for mp in mount_points.iter().skip(1) { - let path = mp.path; let vfsops = mp.fs.clone(); - let message = format!("failed to mount filesystem at {}", path); - info!("mounting {}", path); - root_dir.mount(path, vfsops).expect(&message); + let message = format!("failed to mount filesystem at {}", mp.path); + info!("mounting {}", mp.path); + root_dir.mount(mp.path.clone(), vfsops).expect(&message); } ROOT_DIR.init_by(Arc::new(root_dir)); CURRENT_DIR.init_by(Mutex::new(ROOT_DIR.clone())); - *CURRENT_DIR_PATH.lock() = "/".into(); + *CURRENT_DIR_PATH.lock() = AbsPath::new("/"); } -fn parent_node_of(dir: Option<&VfsNodeRef>, path: &str) -> VfsNodeRef { - if path.starts_with('/') { - ROOT_DIR.clone() - } else { - dir.cloned().unwrap_or_else(|| CURRENT_DIR.lock().clone()) - } +/// Look up a file given an absolute path. +pub(crate) fn lookup(path: &AbsPath) -> AxResult { + ROOT_DIR.clone().lookup(&path.to_rel()) } -pub(crate) fn absolute_path(path: &str) -> AxResult { - if path.starts_with('/') { - Ok(axfs_vfs::path::canonicalize(path)) - } else { - let path = CURRENT_DIR_PATH.lock().clone() + path; - Ok(axfs_vfs::path::canonicalize(&path)) +/// Open a file given an absolute path. +pub fn open_file(path: &AbsPath, opts: &OpenOptions) -> AxResult { + debug!("open file: {} {:?}", path, opts); + if !opts.is_valid() { + return ax_err!(InvalidInput); } -} + let node = match lookup(path) { + Ok(node) => { + if opts.create_new { + return ax_err!(AlreadyExists); + } + node + } + Err(VfsError::NotFound) => { + if !opts.create || !opts.create_new { + return ax_err!(NotFound); + } + create_file(path)?; + lookup(path)? + } + Err(e) => return Err(e), + }; -pub(crate) fn lookup(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); + let attr = node.get_attr()?; + if attr.is_dir() { + return ax_err!(IsADirectory); } - let node = parent_node_of(dir, path).lookup(path)?; - if path.ends_with('/') && !node.get_attr()?.is_dir() { - ax_err!(NotADirectory) - } else { - Ok(node) + let access_cap = opts.into(); + if !perm_to_cap(attr.perm()).contains(access_cap) { + return ax_err!(PermissionDenied); + } + + node.open()?; + if opts.truncate { + node.truncate(0)?; } + Ok(File::new(node, access_cap, opts.append)) } -pub(crate) fn create_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); - } else if path.ends_with('/') { +/// Open a directory given an absolute path. +pub fn open_dir(path: &AbsPath, opts: &OpenOptions) -> AxResult { + debug!("open dir: {}", path); + if !opts.read { + return ax_err!(InvalidInput); + } + if opts.create || opts.create_new || opts.write || opts.append || opts.truncate { + return ax_err!(InvalidInput); + } + let node = lookup(path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { return ax_err!(NotADirectory); } - let parent = parent_node_of(dir, path); - parent.create(path, VfsNodeType::File)?; - parent.lookup(path) + let access_cap = opts.into(); + if !perm_to_cap(attr.perm()).contains(access_cap) { + return ax_err!(PermissionDenied); + } + node.open()?; + Ok(Directory::new(node, access_cap | Cap::EXECUTE)) } -pub(crate) fn create_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - match lookup(dir, path) { +/// Get the file attributes given an absolute path. +pub fn get_attr(path: &AbsPath) -> AxResult { + let node = lookup(path)?; + node.get_attr() +} + +/// Create a file given an absolute path. +pub(crate) fn create_file(path: &AbsPath) -> AxResult { + match lookup(path) { Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => parent_node_of(dir, path).create(path, VfsNodeType::Dir), + Err(AxError::NotFound) => { + ROOT_DIR.create(&path.to_rel(), VfsNodeType::File)?; + lookup(path) + } Err(e) => Err(e), } } -pub(crate) fn create_dir_all(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - match lookup(dir, path) { +/// Create a directory given an absolute path. +pub(crate) fn create_dir(path: &AbsPath) -> AxResult { + match lookup(path) { Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => { - parent_node_of(dir, path).create_recursive(path, VfsNodeType::Dir) - } + Err(AxError::NotFound) => ROOT_DIR.create(&path.to_rel(), VfsNodeType::Dir), Err(e) => Err(e), } } -pub(crate) fn remove_file(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - let node = lookup(dir, path)?; +/// Create a directory recursively given an absolute path. +pub(crate) fn create_dir_all(path: &AbsPath) -> AxResult { + match lookup(path) { + Ok(_) => ax_err!(AlreadyExists), + Err(AxError::NotFound) => ROOT_DIR.create_recursive(&path.to_rel(), VfsNodeType::Dir), + Err(e) => Err(e), + } +} + +/// Rename a file given an absolute path. +pub(crate) fn remove_file(path: &AbsPath) -> AxResult { + let node = lookup(path)?; let attr = node.get_attr()?; if attr.is_dir() { ax_err!(IsADirectory) } else if !attr.perm().owner_writable() { ax_err!(PermissionDenied) } else { - parent_node_of(dir, path).remove(path) + ROOT_DIR.remove(&path.to_rel()) } } -pub(crate) fn remove_dir(dir: Option<&VfsNodeRef>, path: &str) -> AxResult { - if path.is_empty() { - return ax_err!(NotFound); - } - let path_check = path.trim_matches('/'); - if path_check.is_empty() { - return ax_err!(DirectoryNotEmpty); // rm -d '/' - } else if path_check == "." - || path_check == ".." - || path_check.ends_with("/.") - || path_check.ends_with("/..") - { - return ax_err!(InvalidInput); - } - if ROOT_DIR.contains(&absolute_path(path)?) { +/// Remove a directory given an absolute path. +pub(crate) fn remove_dir(path: &AbsPath) -> AxResult { + if ROOT_DIR.contains(path) { return ax_err!(PermissionDenied); } - - let node = lookup(dir, path)?; + let node = lookup(path)?; let attr = node.get_attr()?; if !attr.is_dir() { ax_err!(NotADirectory) } else if !attr.perm().owner_writable() { ax_err!(PermissionDenied) } else { - parent_node_of(dir, path).remove(path) + ROOT_DIR.remove(&path.to_rel()) } } -pub(crate) fn current_dir() -> AxResult { +/// Get current working directory. +pub(crate) fn current_dir<'a>() -> AxResult> { Ok(CURRENT_DIR_PATH.lock().clone()) } -pub(crate) fn set_current_dir(path: &str) -> AxResult { - let mut abs_path = absolute_path(path)?; - if !abs_path.ends_with('/') { - abs_path += "/"; - } - if abs_path == "/" { - *CURRENT_DIR.lock() = ROOT_DIR.clone(); - *CURRENT_DIR_PATH.lock() = "/".into(); - return Ok(()); - } - - let node = lookup(None, &abs_path)?; +/// Set current working directory. +pub(crate) fn set_current_dir(path: AbsPath<'static>) -> AxResult { + let node = lookup(&path)?; let attr = node.get_attr()?; if !attr.is_dir() { ax_err!(NotADirectory) @@ -304,15 +328,16 @@ pub(crate) fn set_current_dir(path: &str) -> AxResult { ax_err!(PermissionDenied) } else { *CURRENT_DIR.lock() = node; - *CURRENT_DIR_PATH.lock() = abs_path; + *CURRENT_DIR_PATH.lock() = path; Ok(()) } } -pub(crate) fn rename(old: &str, new: &str) -> AxResult { - if parent_node_of(None, new).lookup(new).is_ok() { - warn!("dst file already exist, now remove it"); - remove_file(None, new)?; +/// Rename a file given an old and a new absolute path. +pub(crate) fn rename(old: &AbsPath, new: &AbsPath) -> AxResult { + if lookup(new).is_ok() { + ax_err!(AlreadyExists) + } else { + ROOT_DIR.rename(&old.to_rel(), &new.to_rel()) } - parent_node_of(None, old).rename(old, new) } From 35f53494ad737ad5e00a02f98f3b47d553b8a17c Mon Sep 17 00:00:00 2001 From: liujingx Date: Tue, 29 Oct 2024 16:04:53 +0800 Subject: [PATCH 09/36] refactor: implement simple ruxfs operations --- modules/ruxfs/src/api/dir.rs | 23 +- modules/ruxfs/src/api/file.rs | 148 +++++++++++-- modules/ruxfs/src/api/mod.rs | 36 +++- modules/ruxfs/src/fops.rs | 386 +++++++++++++++------------------- modules/ruxfs/src/root.rs | 176 +--------------- 5 files changed, 355 insertions(+), 414 deletions(-) diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index 0c1d0f259..da4793221 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -8,11 +8,12 @@ */ use alloc::string::String; -use axfs_vfs::path::AbsPath; +use axerrno::ax_err; +use axfs_vfs::{path::AbsPath, VfsError}; use axio::Result; use core::fmt; -use super::FileType; +use super::{FileType, OpenOptions}; use crate::fops; /// Iterator over the entries in a directory. @@ -40,9 +41,9 @@ pub struct DirBuilder { impl<'a> ReadDir<'a> { pub(super) fn new(path: &'a AbsPath<'a>) -> Result { - let mut opts = fops::OpenOptions::new(); + let mut opts = OpenOptions::new(); opts.read(true); - let inner = crate::root::open_dir(path, &opts)?; + let inner = fops::open_dir(path, (&opts).into())?; const EMPTY: fops::DirEntry = fops::DirEntry::default(); let dirent_buf = [EMPTY; 31]; Ok(ReadDir { @@ -147,13 +148,23 @@ impl DirBuilder { if self.recursive { self.create_dir_all(path) } else { - crate::root::create_dir(path) + match fops::lookup(path) { + Ok(_) => return ax_err!(AlreadyExists), + Err(VfsError::NotFound) => {} + Err(e) => return ax_err!(e), + } + fops::create_dir(path) } } /// Recursively create a directory and all of its parent components if they /// are missing. pub fn create_dir_all(&self, path: &AbsPath) -> Result<()> { - crate::root::create_dir_all(path) + match fops::lookup(path) { + Ok(_) => return ax_err!(AlreadyExists), + Err(VfsError::NotFound) => {} + Err(e) => return ax_err!(e), + } + fops::create_dir_all(path) } } diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index 82186ef41..87fe137d1 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -7,10 +7,15 @@ * See the Mulan PSL v2 for more details. */ -use axfs_vfs::path::AbsPath; +use crate::fops; +use axerrno::ax_err; +use axfs_vfs::{ + path::{AbsPath, RelPath}, + VfsError, +}; use axio::{prelude::*, Result, SeekFrom}; +use capability::Cap; use core::fmt; -use crate::fops::{self, Directory}; /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. @@ -28,59 +33,170 @@ pub struct File { pub struct Metadata(fops::FileAttr); /// Options and flags which can be used to configure how a file is opened. -#[derive(Clone, Debug)] -pub struct OpenOptions(fops::OpenOptions); +#[derive(Clone)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + _custom_flags: i32, + _mode: u32, +} impl OpenOptions { /// Creates a blank new set of options ready for configuration. pub const fn new() -> Self { - OpenOptions(fops::OpenOptions::new()) + Self { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + _custom_flags: 0, + _mode: 0o666, + } } /// Sets the option for read access. pub fn read(&mut self, read: bool) -> &mut Self { - self.0.read(read); + self.read = read; self } /// Sets the option for write access. pub fn write(&mut self, write: bool) -> &mut Self { - self.0.write(write); + self.write = write; self } /// Sets the option for the append mode. pub fn append(&mut self, append: bool) -> &mut Self { - self.0.append(append); + self.append = append; self } /// Sets the option for truncating a previous file. pub fn truncate(&mut self, truncate: bool) -> &mut Self { - self.0.truncate(truncate); + self.truncate = truncate; self } /// Sets the option to create a new file, or open it if it already exists. pub fn create(&mut self, create: bool) -> &mut Self { - self.0.create(create); + self.create = create; self } /// Sets the option to create a new file, failing if it already exists. pub fn create_new(&mut self, create_new: bool) -> &mut Self { - self.0.create_new(create_new); + self.create_new = create_new; self } + /// Check if the options are valid. + pub const fn is_valid(&self) -> bool { + if !self.read && !self.write && !self.append { + return false; + } + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return false; + } + } + (_, true) => { + if self.truncate && !self.create_new { + return false; + } + } + } + true + } + /// Opens a file at `path` with the options specified by `self`. - pub fn open(&self, path: &AbsPath) -> Result { - crate::root::open_file(&path, &self.0).map(|inner| File { inner }) + pub fn open(&self, path: &str) -> Result { + // Check options + if !self.is_valid() { + return ax_err!(InvalidInput); + } + // Find node + let path = if path.starts_with("/") { + AbsPath::new_canonicalized(path) + } else { + fops::concat_path(&RelPath::new_canonicalized(path)) + }; + // Check flag and attr + let node = match fops::lookup(&path) { + Ok(node) => { + if self.create_new { + return ax_err!(AlreadyExists); + } + node + } + Err(VfsError::NotFound) => { + if !self.create { + return ax_err!(NotFound); + } + fops::create_file(&path)?; + fops::lookup(&path)? + } + Err(e) => return Err(e), + }; + if node.get_attr()?.is_dir() { + return ax_err!(IsADirectory); + } + // Truncate + if self.truncate { + node.truncate(0)?; + } + // Open + fops::open_file(&path, self.into(), self.append).map(|inner| File { inner }) + } +} + +impl From<&OpenOptions> for Cap { + fn from(opts: &OpenOptions) -> Cap { + let mut cap = Cap::empty(); + if opts.read { + cap |= Cap::READ; + } + if opts.write | opts.append { + cap |= Cap::WRITE; + } + cap } +} - /// Opens a directory at `path` with the options specified by `self`. - pub fn open_dir(&self, path: &AbsPath) -> Result { - crate::root::open_dir(&path, &self.0) +impl fmt::Debug for OpenOptions { + #[allow(unused_assignments)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut written = false; + macro_rules! fmt_opt { + ($field: ident, $label: literal) => { + if self.$field { + if written { + write!(f, " | ")?; + } + write!(f, $label)?; + written = true; + } + }; + } + fmt_opt!(read, "READ"); + fmt_opt!(write, "WRITE"); + fmt_opt!(append, "APPEND"); + fmt_opt!(truncate, "TRUNC"); + fmt_opt!(create, "CREATE"); + fmt_opt!(create_new, "CREATE_NEW"); + Ok(()) } } diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index 07a44d477..d41f9e116 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -16,9 +16,13 @@ pub use self::dir::{DirBuilder, DirEntry, ReadDir}; pub use self::file::{File, FileType, Metadata, OpenOptions, Permissions}; use alloc::{string::String, vec::Vec}; +use axerrno::ax_err; use axfs_vfs::path::AbsPath; +use axfs_vfs::VfsError; use axio::{self as io, prelude::*}; +use crate::fops; + /// Returns an iterator over the entries within a directory. pub fn read_dir<'a>(path: &'a AbsPath<'a>) -> io::Result> { ReadDir::new(path) @@ -26,12 +30,12 @@ pub fn read_dir<'a>(path: &'a AbsPath<'a>) -> io::Result> { /// Returns the current working directory as a [`AbsPath`]. pub fn current_dir() -> io::Result> { - crate::root::current_dir() + Ok(fops::current_dir()) } /// Changes the current working directory to the specified path. pub fn set_current_dir(path: AbsPath<'static>) -> io::Result<()> { - crate::root::set_current_dir(path) + fops::set_current_dir(path) } /// Read the entire contents of a file into a bytes vector. @@ -76,12 +80,29 @@ pub fn create_dir_all(path: &AbsPath) -> io::Result<()> { /// Removes an empty directory. pub fn remove_dir(path: &AbsPath) -> io::Result<()> { - crate::root::remove_dir(path) + let node = fops::lookup(path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + return ax_err!(NotADirectory); + } + if !attr.perm().owner_writable() { + return ax_err!(PermissionDenied); + } + // TODO: check empty + fops::remove_dir(path) } /// Removes a file from the filesystem. pub fn remove_file(path: &AbsPath) -> io::Result<()> { - crate::root::remove_file(path) + let node = fops::lookup(path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + return ax_err!(NotADirectory); + } + if !attr.perm().owner_writable() { + return ax_err!(PermissionDenied); + } + fops::remove_file(path) } /// Rename a file or directory to a new name. @@ -89,5 +110,10 @@ pub fn remove_file(path: &AbsPath) -> io::Result<()> { /// /// This only works then the new path is in the same mounted fs. pub fn rename(old: &AbsPath, new: &AbsPath) -> io::Result<()> { - crate::root::rename(old, new) + let old_node = fops::lookup(old)?; + match fops::lookup(new) { + Ok(node) => return ax_err!(AlreadyExists), + Err(VfsError::NotFound) => fops::rename(old, new), + Err(e) => return ax_err!(e), + } } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 27c837ecb..3895a9310 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -9,12 +9,13 @@ //! Low-level filesystem operations. -use axerrno::{ax_err, ax_err_type, AxResult, AxError}; -use axfs_vfs::path::RelPath; -use axfs_vfs::{VfsError, VfsNodeRef, VfsNodeType}; +use axerrno::{ax_err, ax_err_type, AxResult}; +use axfs_vfs::path::{AbsPath, RelPath}; +use axfs_vfs::{VfsNodeRef, VfsNodeType, VfsNodeOps}; use axio::SeekFrom; use capability::{Cap, WithCap}; -use core::fmt; + +use crate::root::{CURRENT_DIR, CURRENT_DIR_PATH, ROOT_DIR}; #[cfg(feature = "myfs")] pub use crate::dev::Disk; @@ -44,83 +45,6 @@ pub struct Directory { entry_idx: usize, } -/// Options and flags which can be used to configure how a file is opened. -#[derive(Clone)] -pub struct OpenOptions { - // generic - pub read: bool, - pub write: bool, - pub append: bool, - pub truncate: bool, - pub create: bool, - pub create_new: bool, - // system-specific - _custom_flags: i32, - _mode: u32, -} - -impl OpenOptions { - /// Creates a blank new set of options ready for configuration. - pub const fn new() -> Self { - Self { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - _custom_flags: 0, - _mode: 0o666, - } - } - /// Sets the option for read access. - pub fn read(&mut self, read: bool) { - self.read = read; - } - /// Sets the option for write access. - pub fn write(&mut self, write: bool) { - self.write = write; - } - /// Sets the option for the append mode. - pub fn append(&mut self, append: bool) { - self.append = append; - } - /// Sets the option for truncating a previous file. - pub fn truncate(&mut self, truncate: bool) { - self.truncate = truncate; - } - /// Sets the option to create a new file, or open it if it already exists. - pub fn create(&mut self, create: bool) { - self.create = create; - } - /// Sets the option to create a new file, failing if it already exists. - pub fn create_new(&mut self, create_new: bool) { - self.create_new = create_new; - } - - pub const fn is_valid(&self) -> bool { - if !self.read && !self.write && !self.append { - return false; - } - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return false; - } - } - (_, true) => { - if self.truncate && !self.create_new { - return false; - } - } - } - true - } -} - impl File { /// Create an opened file. pub fn new(node: VfsNodeRef, cap: Cap, is_append: bool) -> Self { @@ -222,120 +146,86 @@ impl Directory { } } + /// Gets the file attributes. + pub fn get_attr(&self) -> AxResult { + self.node.access(Cap::empty())?.get_attr() + } + + /// Looks up a child node by name. + pub fn lookup(&self, path: &RelPath) -> AxResult { + self.access_node()?.clone().lookup(path) + } + /// Gets the file attributes of the file at the path relative to this directory. /// Returns a [`FileAttr`] object. - pub fn get_child_attr_at(&self, path: &RelPath) -> AxResult { - self.access_node()?.clone().lookup(path)?.get_attr() + pub fn get_child_attr(&self, path: &RelPath) -> AxResult { + let node = self.lookup(path)?; + node.get_attr() } /// Opens a directory at the path relative to this directory. Returns a /// [`Directory`] object. - pub fn open_dir_at(&self, path: &RelPath, opts: &OpenOptions) -> AxResult { + /// + /// This function will not check if the path exists or is a directory, + /// check it with [`get_child_attr`] first. + pub fn open_dir(&self, path: &RelPath, cap: Cap) -> AxResult { debug!("open dir: {}", path); - if !opts.read { - return ax_err!(InvalidInput); - } - if opts.create || opts.create_new || opts.write || opts.append || opts.truncate { - return ax_err!(InvalidInput); - } - let node = self.access_node()?.clone().lookup(path)?; + let node = self.lookup(path)?; let attr = node.get_attr()?; - if !attr.is_dir() { - return ax_err!(NotADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { + if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); } node.open()?; - Ok(Self::new(node, access_cap | Cap::EXECUTE)) + Ok(Self::new(node, cap | Cap::EXECUTE)) } /// Opens a file at the path relative to this directory. Returns a [`File`] /// object. - pub fn open_file_at(&self, path: &RelPath, opts: &OpenOptions) -> AxResult { - debug!("open file: {} {:?}", path, opts); - if !opts.is_valid() { - return ax_err!(InvalidInput); - } - let node = match self.access_node()?.clone().lookup(path) { - Ok(node) => { - if opts.create_new { - return ax_err!(AlreadyExists); - } - node - } - Err(VfsError::NotFound) => { - if !opts.create || !opts.create_new { - return ax_err!(NotFound); - } - self.access_node()?.clone().create(path, VfsNodeType::File)?; - self.access_node()?.clone().lookup(path)? - } - Err(e) => return Err(e), - }; - + /// + /// This function will not check if the path exists or is a file, check it + /// with [`get_child_attr`] first. + pub fn open_file(&self, path: &RelPath, cap: Cap, append: bool) -> AxResult { + debug!("open file: {}", path); + let node = self.lookup(path)?; let attr = node.get_attr()?; - if attr.is_dir() { - return ax_err!(IsADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { + if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); } - node.open()?; - if opts.truncate { - node.truncate(0)?; - } - Ok(File::new(node, access_cap, opts.append)) + Ok(File::new(node, cap, append)) } /// Creates an empty file at the path relative to this directory. - pub fn create_file(&self, path: &RelPath) -> AxResult { - match self.access_node()?.clone().lookup(path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => { - self.access_node()?.clone().create(path, VfsNodeType::File)?; - self.access_node()?.clone().lookup(path) - } - Err(e) => Err(e), - } + /// + /// This function will not check if the path exists, check it with + /// [`lookup`] first. + pub fn create_file(&self, path: &RelPath) -> AxResult { + self.access_node()?.clone().create(path, VfsNodeType::File) } /// Creates an empty directory at the path relative to this directory. + /// + /// This function will not check if the path exists, check it with + /// [`lookup`] first. pub fn create_dir(&self, path: &RelPath) -> AxResult { - match self.access_node()?.clone().lookup(path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => self.access_node()?.create(path, VfsNodeType::Dir), - Err(e) => Err(e), - } + self.access_node()?.clone().create(path, VfsNodeType::Dir) } - /// Removes a file at the path relative to this directory. - pub fn remove_file(&self, path: &RelPath) -> AxResult { - let node = self.access_node()?.clone().lookup(path)?; - let attr = node.get_attr()?; - if attr.is_dir() { - ax_err!(IsADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - self.access_node()?.remove(path) - } + /// Removes a file (or directory) at the path relative to this directory. + /// + /// This function will not check if the file (or directory) exits or removeable, + /// check it with [`lookup`] first. + pub fn remove(&self, path: &RelPath) -> AxResult { + self.access_node()?.remove(path) } - /// Removes a directory at the path relative to this directory. - pub fn remove_dir(&self, path: &RelPath) -> AxResult { - let node = self.access_node()?.clone().lookup(path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - ax_err!(NotADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - self.access_node()?.remove(path) - } + /// Rename a file or directory to a new name. This only works then the new path + /// is in the same mounted fs. + /// + /// This function will not check if the old path or new path exists, check it with + /// [`lookup`] first. + pub fn rename(&self, old: &RelPath, new: &RelPath) -> AxResult { + self.access_node()?.rename(old, new) } /// Reads directory entries starts from the current position into the @@ -361,23 +251,123 @@ impl Directory { pub fn set_entry_idx(&mut self, idx: usize) { self.entry_idx = idx; } +} - /// Rename a file or directory to a new name. - /// Delete the original file if `old` already exists. - /// - /// This only works then the new path is in the same mounted fs. - pub fn rename(&self, old: &RelPath, new: &RelPath) -> AxResult { - if self.access_node()?.clone().lookup(new).is_ok() { - warn!("dst file already exist, now remove it"); - ax_err!(AlreadyExists) - } else { - self.access_node()?.rename(old, new) - } +/* File operations with absolute path */ + +/// Look up a file given an absolute path. +pub fn lookup(path: &AbsPath) -> AxResult { + ROOT_DIR.clone().lookup(&path.to_rel()) +} + +/// Get the file attributes given an absolute path. +pub fn get_attr(path: &AbsPath) -> AxResult { + let node = lookup(path)?; + node.get_attr() +} + +/// Open a file given an absolute path. +/// +/// This function will not check if the file exists or is a file. +/// Call [`lookup`] to check if first. +pub fn open_file(path: &AbsPath, cap: Cap, append: bool) -> AxResult { + debug!("open file: {}", path); + let node = lookup(path)?; + let attr = node.get_attr()?; + if !perm_to_cap(attr.perm()).contains(cap) { + return ax_err!(PermissionDenied); + } + node.open()?; + Ok(File::new(node, cap, append)) +} + +/// Open a directory given an absolute path. +/// +/// This function will not check if the path exists or is a directory, +/// check it with [`get_attr`] first. +pub fn open_dir(path: &AbsPath, cap: Cap) -> AxResult { + debug!("open dir: {}", path); + let node = lookup(path)?; + let attr = node.get_attr()?; + if !perm_to_cap(attr.perm()).contains(cap) { + return ax_err!(PermissionDenied); + } + node.open()?; + Ok(Directory::new(node, cap | Cap::EXECUTE)) +} + +/// Create a file given an absolute path. +/// +/// This function will not check if the file exists, check it with [`lookup`] first. +pub fn create_file(path: &AbsPath) -> AxResult { + ROOT_DIR.create(&path.to_rel(), VfsNodeType::File) +} + +/// Create a directory given an absolute path. +/// +/// This function will not check if the directory exists, check it with [`lookup`] first. +pub fn create_dir(path: &AbsPath) -> AxResult { + ROOT_DIR.create(&path.to_rel(), VfsNodeType::Dir) +} + +/// Create a directory recursively given an absolute path. +/// +/// This function will not check if the directory exists, check it with [`lookup`] first. +pub fn create_dir_all(path: &AbsPath) -> AxResult { + ROOT_DIR.create_recursive(&path.to_rel(), VfsNodeType::Dir) +} + +/// Remove a file given an absolute path. +/// +/// This function will not check if the file exits or removeable, +/// check it with [`lookup`] first. +pub fn remove_file(path: &AbsPath) -> AxResult { + ROOT_DIR.remove(&path.to_rel()) +} + +/// Remove a directory given an absolute path. +/// +/// This function will not check if the directory exists or is empty, +/// check it with [`lookup`] first. +pub fn remove_dir(path: &AbsPath) -> AxResult { + if ROOT_DIR.contains(path) { + return ax_err!(PermissionDenied); } + ROOT_DIR.remove(&path.to_rel()) +} - /// Gets the file attributes. - pub fn get_attr(&self) -> AxResult { - self.node.access(Cap::empty())?.get_attr() +/// Rename a file given an old and a new absolute path. +/// +/// This function will not check if the old path or new path exists, check it with +/// [`lookup`] first. +pub fn rename(old: &AbsPath, new: &AbsPath) -> AxResult { + ROOT_DIR.rename(&old.to_rel(), &new.to_rel()) +} + +/// Get current working directory. +pub fn current_dir<'a>() -> AbsPath<'a> { + CURRENT_DIR_PATH.lock().clone() +} + +/// Concatenate a path to the current working directory. +pub fn concat_path(path: &RelPath) -> AbsPath<'static> { + current_dir().join(path) +} + +/// Set current working directory. +/// +/// Returns error if the path does not exist or is not a directory. +pub fn set_current_dir(path: AbsPath<'static>) -> AxResult { + let node = lookup(&path)?; + let attr = node.get_attr()?; + if !attr.is_dir() { + ax_err!(NotADirectory) + } else if !attr.perm().owner_executable() { + ax_err!(PermissionDenied) + } else { + *CURRENT_DIR.lock() = node; + *CURRENT_DIR_PATH.lock() = path; + Ok(()) } } @@ -393,44 +383,6 @@ impl Drop for Directory { } } -impl fmt::Debug for OpenOptions { - #[allow(unused_assignments)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut written = false; - macro_rules! fmt_opt { - ($field: ident, $label: literal) => { - if self.$field { - if written { - write!(f, " | ")?; - } - write!(f, $label)?; - written = true; - } - }; - } - fmt_opt!(read, "READ"); - fmt_opt!(write, "WRITE"); - fmt_opt!(append, "APPEND"); - fmt_opt!(truncate, "TRUNC"); - fmt_opt!(create, "CREATE"); - fmt_opt!(create_new, "CREATE_NEW"); - Ok(()) - } -} - -impl From<&OpenOptions> for Cap { - fn from(opts: &OpenOptions) -> Cap { - let mut cap = Cap::empty(); - if opts.read { - cap |= Cap::READ; - } - if opts.write | opts.append { - cap |= Cap::WRITE; - } - cap - } -} - pub fn perm_to_cap(perm: FilePerm) -> Cap { let mut cap = Cap::empty(); if perm.owner_readable() { diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index d73fcf804..a63219af6 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -12,22 +12,18 @@ //! TODO: it doesn't work very well if the mount points have containment relationships. use alloc::{format, sync::Arc, vec::Vec}; -use axerrno::{ax_err, AxError, AxResult}; +use axerrno::{ax_err, AxResult}; use axfs_vfs::{ path::{AbsPath, RelPath}, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult, }; use axsync::Mutex; -use capability::Cap; use lazy_init::LazyInit; -use crate::{ - api::FileType, - fops::{perm_to_cap, Directory, File, OpenOptions}, -}; +use crate::api::FileType; -static CURRENT_DIR_PATH: Mutex = Mutex::new(AbsPath::new("/")); -static CURRENT_DIR: LazyInit> = LazyInit::new(); +pub(crate) static CURRENT_DIR_PATH: Mutex = Mutex::new(AbsPath::new("/")); +pub(crate) static CURRENT_DIR: LazyInit> = LazyInit::new(); /// mount point information pub struct MountPoint { @@ -35,12 +31,12 @@ pub struct MountPoint { fs: Arc, } -struct RootDirectory { +pub(crate) struct RootDirectory { main_fs: Arc, mounts: Vec, } -static ROOT_DIR: LazyInit> = LazyInit::new(); +pub(crate) static ROOT_DIR: LazyInit> = LazyInit::new(); impl MountPoint { /// create new MountPoint from data @@ -181,163 +177,3 @@ pub(crate) fn init_rootfs(mount_points: Vec) { CURRENT_DIR.init_by(Mutex::new(ROOT_DIR.clone())); *CURRENT_DIR_PATH.lock() = AbsPath::new("/"); } - -/// Look up a file given an absolute path. -pub(crate) fn lookup(path: &AbsPath) -> AxResult { - ROOT_DIR.clone().lookup(&path.to_rel()) -} - -/// Open a file given an absolute path. -pub fn open_file(path: &AbsPath, opts: &OpenOptions) -> AxResult { - debug!("open file: {} {:?}", path, opts); - if !opts.is_valid() { - return ax_err!(InvalidInput); - } - let node = match lookup(path) { - Ok(node) => { - if opts.create_new { - return ax_err!(AlreadyExists); - } - node - } - Err(VfsError::NotFound) => { - if !opts.create || !opts.create_new { - return ax_err!(NotFound); - } - create_file(path)?; - lookup(path)? - } - Err(e) => return Err(e), - }; - - let attr = node.get_attr()?; - if attr.is_dir() { - return ax_err!(IsADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { - return ax_err!(PermissionDenied); - } - - node.open()?; - if opts.truncate { - node.truncate(0)?; - } - Ok(File::new(node, access_cap, opts.append)) -} - -/// Open a directory given an absolute path. -pub fn open_dir(path: &AbsPath, opts: &OpenOptions) -> AxResult { - debug!("open dir: {}", path); - if !opts.read { - return ax_err!(InvalidInput); - } - if opts.create || opts.create_new || opts.write || opts.append || opts.truncate { - return ax_err!(InvalidInput); - } - let node = lookup(path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - return ax_err!(NotADirectory); - } - let access_cap = opts.into(); - if !perm_to_cap(attr.perm()).contains(access_cap) { - return ax_err!(PermissionDenied); - } - node.open()?; - Ok(Directory::new(node, access_cap | Cap::EXECUTE)) -} - -/// Get the file attributes given an absolute path. -pub fn get_attr(path: &AbsPath) -> AxResult { - let node = lookup(path)?; - node.get_attr() -} - -/// Create a file given an absolute path. -pub(crate) fn create_file(path: &AbsPath) -> AxResult { - match lookup(path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => { - ROOT_DIR.create(&path.to_rel(), VfsNodeType::File)?; - lookup(path) - } - Err(e) => Err(e), - } -} - -/// Create a directory given an absolute path. -pub(crate) fn create_dir(path: &AbsPath) -> AxResult { - match lookup(path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => ROOT_DIR.create(&path.to_rel(), VfsNodeType::Dir), - Err(e) => Err(e), - } -} - -/// Create a directory recursively given an absolute path. -pub(crate) fn create_dir_all(path: &AbsPath) -> AxResult { - match lookup(path) { - Ok(_) => ax_err!(AlreadyExists), - Err(AxError::NotFound) => ROOT_DIR.create_recursive(&path.to_rel(), VfsNodeType::Dir), - Err(e) => Err(e), - } -} - -/// Rename a file given an absolute path. -pub(crate) fn remove_file(path: &AbsPath) -> AxResult { - let node = lookup(path)?; - let attr = node.get_attr()?; - if attr.is_dir() { - ax_err!(IsADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - ROOT_DIR.remove(&path.to_rel()) - } -} - -/// Remove a directory given an absolute path. -pub(crate) fn remove_dir(path: &AbsPath) -> AxResult { - if ROOT_DIR.contains(path) { - return ax_err!(PermissionDenied); - } - let node = lookup(path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - ax_err!(NotADirectory) - } else if !attr.perm().owner_writable() { - ax_err!(PermissionDenied) - } else { - ROOT_DIR.remove(&path.to_rel()) - } -} - -/// Get current working directory. -pub(crate) fn current_dir<'a>() -> AxResult> { - Ok(CURRENT_DIR_PATH.lock().clone()) -} - -/// Set current working directory. -pub(crate) fn set_current_dir(path: AbsPath<'static>) -> AxResult { - let node = lookup(&path)?; - let attr = node.get_attr()?; - if !attr.is_dir() { - ax_err!(NotADirectory) - } else if !attr.perm().owner_executable() { - ax_err!(PermissionDenied) - } else { - *CURRENT_DIR.lock() = node; - *CURRENT_DIR_PATH.lock() = path; - Ok(()) - } -} - -/// Rename a file given an old and a new absolute path. -pub(crate) fn rename(old: &AbsPath, new: &AbsPath) -> AxResult { - if lookup(new).is_ok() { - ax_err!(AlreadyExists) - } else { - ROOT_DIR.rename(&old.to_rel(), &new.to_rel()) - } -} From 29d9c53c49bff92b39b58cf26c43ee9c6be2c1e0 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 31 Oct 2024 11:04:30 +0800 Subject: [PATCH 10/36] refacotr: fs posix api --- Cargo.lock | 1 + api/ruxos_posix_api/Cargo.toml | 1 + api/ruxos_posix_api/src/imp/fs.rs | 311 +++++++++++++++++------------- apps/custom/harness/src/main.rs | 2 +- crates/axfs_vfs/src/path.rs | 9 +- modules/ruxfs/src/api/dir.rs | 3 +- modules/ruxfs/src/api/file.rs | 4 +- modules/ruxfs/src/api/mod.rs | 4 +- modules/ruxfs/src/fops.rs | 38 +--- 9 files changed, 201 insertions(+), 172 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f1e3213c..06acda278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1811,6 +1811,7 @@ dependencies = [ "axsync", "bindgen 0.66.1", "bitflags 2.6.0", + "capability", "cfg-if", "crate_interface", "elf", diff --git a/api/ruxos_posix_api/Cargo.toml b/api/ruxos_posix_api/Cargo.toml index 6ae5c1cea..67f7dbce9 100644 --- a/api/ruxos_posix_api/Cargo.toml +++ b/api/ruxos_posix_api/Cargo.toml @@ -61,6 +61,7 @@ lazy_static = { version = "1.4", features = ["spin_no_std"] } flatten_objects = { path = "../../crates/flatten_objects" } page_table = { path = "../../crates/page_table" } crate_interface = "0.1.1" +capability = { path = "../../crates/capability" } cfg-if = "1.0" elf = { version = "0.7", default-features = false } diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 075c72989..09eaf1409 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -7,22 +7,21 @@ * See the Mulan PSL v2 for more details. */ -use alloc::{borrow::Cow, string::String, sync::Arc}; +use alloc::{sync::Arc, string::ToString}; use core::ffi::{c_char, c_int, c_long, c_void, CStr}; use axerrno::{LinuxError, LinuxResult}; use axio::{Error, PollState, SeekFrom}; use axsync::Mutex; +use capability::Cap; use ruxfdtable::{FileLike, RuxStat}; use ruxfs::{ - api::{current_dir, set_current_dir}, - fops::{DirEntry, OpenOptions}, + fops::{self, DirEntry}, AbsPath, RelPath, }; use super::fd_ops::get_file_like; use crate::ctypes; -use alloc::vec::Vec; pub struct File { pub(crate) inner: Mutex, @@ -172,31 +171,13 @@ impl FileLike for Directory { } } -/// Convert open flags to [`OpenOptions`]. -fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { - let flags = flags as u32; - let mut options = OpenOptions::new(); +/// Convert open flags to [`Cap`]. +fn flags_to_cap(flags: u32) -> Cap { match flags & 0b11 { - ctypes::O_RDONLY => options.read(true), - ctypes::O_WRONLY => options.write(true), - _ => { - options.read(true); - options.write(true) - } - }; - if flags & ctypes::O_APPEND != 0 { - options.append(true); - } - if flags & ctypes::O_TRUNC != 0 { - options.truncate(true); - } - if flags & ctypes::O_CREAT != 0 { - options.create(true); - } - if flags & ctypes::O_EXEC != 0 { - options.create_new(true); + ctypes::O_RDONLY => Cap::READ, + ctypes::O_WRONLY => Cap::WRITE, + _ => Cap::READ | Cap::WRITE, } - options } /// Open a file by `filename` and insert it into the file descriptor table. @@ -204,88 +185,123 @@ fn flags_to_options(flags: c_int, _mode: ctypes::mode_t) -> OpenOptions { /// Return its index in the file table (`fd`). Return `EMFILE` if it already /// has the maximum number of files open. pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let filename = char_ptr_to_path(filename); + let path = char_ptr_to_path(filename); + let flags = flags as u32; debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode); + syscall_body!(sys_open, { - let options = flags_to_options(flags, mode); - let file = ruxfs::root::open_file(&filename?.absolute(), &options)?; + let path = path?; + // Check flag and attr + let node = match fops::lookup(&path.to_abs()) { + Ok(node) => { + if flags & ctypes::O_EXCL != 0 { + return Err(LinuxError::EEXIST); + } + node + } + Err(Error::NotFound) => { + if !(flags & ctypes::O_CREAT != 0) { + return Err(LinuxError::ENOENT); + } + fops::create_file(&path.to_abs())?; + fops::lookup(&path.to_abs())? + } + Err(e) => return Err(e.into()), + }; + if node.get_attr()?.is_dir() { + return Err(LinuxError::EISDIR); + } + // Truncate + if flags & ctypes::O_TRUNC != 0 { + node.truncate(0)?; + } + // Open + let append = flags & ctypes::O_APPEND != 0; + let file = fops::open_file(node, flags_to_cap(flags), append)?; File::new(file).add_to_fd_table() }) } /// Open a file under a specific dir -pub fn sys_openat(fd: usize, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { +pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { let path = char_ptr_to_path(path); - let fd: c_int = fd as c_int; + let flags = flags as u32; + let cap = flags_to_cap(flags); debug!( "sys_openat <= {}, {:?}, {:#o}, {:#o}", fd, path, flags, mode ); + syscall_body!(sys_openat, { let path = path?; - let options = flags_to_options(flags, mode); - let dflag = flags as u32 & ctypes::O_DIRECTORY != 0; - let cflag = flags as u32 & ctypes::O_CREAT != 0; - - let attr = match &path { - Path::Absolute(path) => { - ruxfs::root::get_attr(&path) - } - Path::Relative(path) => { - if fd == ctypes::AT_FDCWD { - ruxfs::root::get_attr(¤t_dir()?.join(&path)) - } else { - let dir = Directory::from_fd(fd)?; - let attr = dir.inner.lock().get_child_attr_at(&path); - attr - } - } + let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; + // Get child node + let lookup_res = if absolute { + fops::lookup(&path.to_abs()) + } else { + let dir = Directory::from_fd(fd)?; + let node = dir.inner.lock().lookup(&path.to_rel()); + node }; - // Check child attributes first - let is_dir = match attr { - Ok(inner) => { - if !inner.is_dir() && dflag { + // Check node attributes and handle not found + let node = match lookup_res { + Ok(node) => { + let attr = node.get_attr()?; + // Node exists but O_EXCL is set + if flags & ctypes::O_EXCL != 0 { + return Err(LinuxError::EEXIST); + } + // Node is not a directory but O_DIRECTORY is set + if !attr.is_dir() && (flags & ctypes::O_DIRECTORY != 0) { return Err(LinuxError::ENOTDIR); } - inner.is_dir() + // Truncate + if attr.is_file() && (flags & ctypes::O_TRUNC != 0) { + node.truncate(0)?; + } + node } Err(Error::NotFound) => { - if !cflag { + // O_CREAT is not set or O_DIRECTORY is set + if (flags & ctypes::O_DIRECTORY != 0) || (flags & ctypes::O_CREAT == 0) { return Err(LinuxError::ENOENT); } - dflag + // Create file + if absolute { + let path = path.to_abs(); + fops::create_file(&path)?; + fops::lookup(&path)? + } else { + let path = path.to_rel(); + let dir = Directory::from_fd(fd)?; + dir.inner.lock().create_file(&path)?; + let node = dir.inner.lock().lookup(&path)?; + node + } } Err(e) => return Err(e.into()), }; // Open file or directory - match path { - Path::Absolute(path) => { - if is_dir { - let dir = ruxfs::root::open_dir(&path, &options)?; - Directory::new(dir).add_to_fd_table() - } else { - let file = ruxfs::root::open_file(&path, &options)?; - File::new(file).add_to_fd_table() - } + let append = flags & ctypes::O_APPEND != 0; + match (absolute, node.get_attr()?.is_dir()) { + (true, true) => { + let dir = fops::open_dir(node, cap)?; + Directory::new(dir).add_to_fd_table() } - Path::Relative(ref path) => { - if fd == ctypes::AT_FDCWD { - if is_dir { - let dir = ruxfs::root::open_dir(¤t_dir()?.join(&path), &options)?; - Directory::new(dir).add_to_fd_table() - } else { - let file = ruxfs::root::open_file(¤t_dir()?.join(&path), &options)?; - File::new(file).add_to_fd_table() - } - } else { - if is_dir { - let dir = Directory::from_fd(fd)?.inner.lock().open_dir_at(&path, &options)?; - Directory::new(dir).add_to_fd_table() - } else { - let file = Directory::from_fd(fd)?.inner.lock().open_file_at(&path, &options)?; - File::new(file).add_to_fd_table() - } - } + (true, false) => { + let file = fops::open_file(node, cap, append)?; + File::new(file).add_to_fd_table() + } + (false, true) => { + let dir = Directory::from_fd(fd)?.inner.lock().open_dir(node, cap)?; + Directory::new(dir).add_to_fd_table() + } + (false, false) => { + let file = Directory::from_fd(fd)? + .inner + .lock() + .open_file(node, cap, append)?; + File::new(file).add_to_fd_table() } } }) @@ -374,10 +390,15 @@ pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_in if buf.is_null() { return Err(LinuxError::EFAULT); } - let mut options = OpenOptions::new(); - options.read(true); - let file = ruxfs::root::open_file(&path?.absolute(), &options)?; - let st: ctypes::stat = File::new(file).stat()?.into(); + let node = fops::lookup(&path?.to_abs())?; + let attr = node.get_attr()?; + let st = if attr.is_dir() { + let dir = fops::open_dir(node, Cap::READ)?; + Directory::new(dir).stat()?.into() + } else { + let file = fops::open_file(node, Cap::READ, false)?; + File::new(file).stat()?.into() + }; #[cfg(not(feature = "musl"))] { @@ -462,7 +483,7 @@ pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes:: /// `newfstatat` used by A64 pub unsafe fn sys_newfstatat( - _fd: c_int, + fd: c_int, path: *const c_char, kst: *mut ctypes::kstat, flag: c_int, @@ -470,16 +491,27 @@ pub unsafe fn sys_newfstatat( let path = char_ptr_to_path(path); debug!( "sys_newfstatat <= fd: {}, path: {:?}, flag: {:x}", - _fd, path, flag + fd, path, flag ); syscall_body!(sys_newfstatat, { + let path = path?; if kst.is_null() { return Err(LinuxError::EFAULT); } - let mut options = OpenOptions::new(); - options.read(true); - let file = ruxfs::root::open_file(&path?.absolute(), &options)?; - let st = File::new(file).stat()?; + let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; + let node = if absolute { + fops::lookup(&path.to_abs())? + } else { + Directory::from_fd(fd)? + .inner + .lock() + .lookup(&path.to_rel())? + }; + let st = if node.get_attr()?.is_dir() { + Directory::new(fops::open_dir(node, Cap::READ)?).stat()? + } else { + File::new(fops::open_file(node, Cap::READ, false)?).stat()? + }; unsafe { (*kst).st_dev = st.st_dev; (*kst).st_ino = st.st_ino; @@ -503,7 +535,7 @@ pub fn sys_getcwd(buf: *mut c_char, size: usize) -> c_int { return Err(LinuxError::EINVAL); } let dst = unsafe { core::slice::from_raw_parts_mut(buf as *mut u8, size as _) }; - let cwd = ruxfs::api::current_dir()?; + let cwd = fops::current_dir(); let cwd = cwd.as_bytes(); if cwd.len() < size { dst[..cwd.len()].copy_from_slice(cwd); @@ -524,7 +556,19 @@ pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int { let old_path = char_ptr_to_path(old)?; let new_path = char_ptr_to_path(new)?; debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path); - ruxfs::api::rename(&old_path.absolute(), &new_path.absolute())?; + if old_path == new_path { + return Ok(0); + } + match fops::lookup(&old_path.to_abs()) { + Ok(_) => {} + Err(e) => return Err(e.into()), + } + match fops::lookup(&new_path.to_abs()) { + Ok(_) => return Err(LinuxError::EEXIST), + Err(Error::NotFound) => {} + Err(e) => return Err(e.into()), + } + fops::rename(&old_path.to_abs(), &new_path.to_abs())?; Ok(0) }) } @@ -542,7 +586,7 @@ pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const assert_eq!(oldfd, ctypes::AT_FDCWD as c_int); assert_eq!(newfd, ctypes::AT_FDCWD as c_int); syscall_body!(sys_renameat, { - ruxfs::api::rename(&old_path?.absolute(), &new_path?.absolute())?; + fops::rename(&old_path?.to_abs(), &new_path?.to_abs())?; Ok(0) }) } @@ -552,7 +596,7 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { syscall_body!(sys_rmdir, { let path = char_ptr_to_path(pathname)?; debug!("sys_rmdir <= path: {:?}", path); - ruxfs::api::remove_dir(&path.absolute())?; + fops::remove_dir(&path.to_abs())?; Ok(0) }) } @@ -562,7 +606,7 @@ pub fn sys_unlink(pathname: *const c_char) -> c_int { syscall_body!(sys_unlink, { let path = char_ptr_to_path(pathname)?; debug!("sys_unlink <= path: {:?}", path); - ruxfs::api::remove_file(&path.absolute())?; + fops::remove_file(&path.to_abs())?; Ok(0) }) } @@ -587,13 +631,11 @@ pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { syscall_body!(sys_mkdir, { let path = char_ptr_to_path(pathname)?; debug!("sys_mkdir <= path: {:?}, mode: {:?}", path, mode); - match path { - Path::Absolute(p) => { - ruxfs::api::create_dir(&p)? - } - Path::Relative(p) => { - ruxfs::api::create_dir(¤t_dir()?.join(&p))? - } + let node = fops::lookup(&path.to_abs()); + match node { + Ok(_) => return Err(LinuxError::EEXIST), + Err(Error::NotFound) => fops::create_dir(&path.to_abs())?, + Err(e) => return Err(e.into()), } Ok(0) }) @@ -653,19 +695,6 @@ type LinuxDirent64 = ctypes::dirent; /// `d_ino` + `d_off` + `d_reclen` + `d_type` const DIRENT64_FIXED_SIZE: usize = 19; -fn convert_name_to_array(name: &[u8]) -> [i8; 256] { - let mut array = [0i8; 256]; - let len = name.len(); - let name_ptr = name.as_ptr() as *const i8; - let array_ptr = array.as_mut_ptr(); - - unsafe { - core::ptr::copy_nonoverlapping(name_ptr, array_ptr, len); - } - - array -} - /// Read directory entries from a directory file descriptor. /// /// TODO: check errors, change 280 to a special value @@ -777,33 +806,42 @@ pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: /// changes the current working directory to the directory specified in path. pub fn sys_chdir(path: *const c_char) -> c_int { - let p = char_ptr_to_path(path).unwrap(); - debug!("sys_chdir <= path: {}", p); + let path = char_ptr_to_path(path); + debug!("sys_chdir <= path: {:?}", path); syscall_body!(sys_chdir, { - match p { - Path::Absolute(p) => set_current_dir(p)?, - Path::Relative(p) => set_current_dir(current_dir()?.join(&p))?, - } + let path = path?; + fops::set_current_dir(AbsPath::new_owned(path.to_abs().to_string()))?; Ok(0) }) } /// Generic path type. -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum Path<'a> { Absolute(AbsPath<'a>), Relative(RelPath<'a>), } impl<'a> Path<'a> { - /// Transforms the path into a `AbsPath`. - /// + /// Translate the path into a `RelPath`. + /// + /// * If the path is already a relative path, it is returned as is. + /// * If the path is an absolute path, its root is stripped. + pub fn to_rel(&'a self) -> RelPath<'a> { + match self { + Path::Absolute(p) => p.to_rel(), + Path::Relative(p) => p.clone(), + } + } + + /// Translate the path into a `AbsPath`. + /// /// * If the path is already an absolute path, it is returned as is. /// * If the path is a relative path, it is resolved against the current working directory. - pub fn absolute(self) -> AbsPath<'a> { + pub fn to_abs(&'a self) -> AbsPath<'a> { match self { - Path::Absolute(p) => p, - Path::Relative(p) => current_dir().unwrap().join(&p), + Path::Absolute(p) => p.clone(), + Path::Relative(p) => fops::current_dir().join(&p), } } } @@ -818,14 +856,19 @@ impl core::fmt::Display for Path<'_> { } /// from char_ptr get path_str -pub fn char_ptr_to_path<'a>(ptr: *const c_char) -> LinuxResult> { +pub fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { if ptr.is_null() { return Err(LinuxError::EFAULT); } - let path = unsafe { + unsafe { let cstr = CStr::from_ptr(ptr); - cstr.to_str().map_err(|_| LinuxError::EINVAL)? - }; + cstr.to_str().map_err(|_| LinuxError::EINVAL) + } +} + +/// from char_ptr get wrapped path type +fn char_ptr_to_path<'a>(ptr: *const c_char) -> LinuxResult> { + let path = char_ptr_to_path_str(ptr)?; if path.starts_with('/') { Ok(Path::Absolute(AbsPath::new_canonicalized(path))) } else { diff --git a/apps/custom/harness/src/main.rs b/apps/custom/harness/src/main.rs index 0fdd10eb9..0365d4a3a 100644 --- a/apps/custom/harness/src/main.rs +++ b/apps/custom/harness/src/main.rs @@ -31,7 +31,7 @@ harness_command!(km_command::fs, Openat, { let mut path = get!(path).clone(); path.push('\0').unwrap(); sys_openat( - get!(dirfd) as usize, + get!(dirfd) as i32, path.as_ptr() as *const i8, get!(flags).bits() as i32, get!(mode).bits(), diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index ce63d89da..6245d9557 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -69,11 +69,16 @@ pub fn canonicalize(path: &str) -> String { pub struct AbsPath<'a>(Cow<'a, str>); impl<'a> AbsPath<'a> { - /// Simply wrap a string into a `AbsPath`. + /// Simply wrap a str slice into a `AbsPath`. pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } + /// Simply wrap a string into a `AbsPath`. + pub fn new_owned(path: String) -> Self { + Self(Cow::Owned(path)) + } + /// Parse and canonicalize an absolute path from a string. pub fn new_canonicalized(path: &str) -> Self { if !path.starts_with('/') { @@ -119,7 +124,7 @@ impl core::fmt::Display for AbsPath<'_> { /// - "a/b/c" /// /// Using `Cow` type to avoid unnecessary allocations. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct RelPath<'a>(Cow<'a, str>); impl<'a> RelPath<'a> { diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index da4793221..0ddf30b23 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -43,7 +43,8 @@ impl<'a> ReadDir<'a> { pub(super) fn new(path: &'a AbsPath<'a>) -> Result { let mut opts = OpenOptions::new(); opts.read(true); - let inner = fops::open_dir(path, (&opts).into())?; + let node = fops::lookup(path)?; + let inner = fops::open_dir(node, (&opts).into())?; const EMPTY: fops::DirEntry = fops::DirEntry::default(); let dirent_buf = [EMPTY; 31]; Ok(ReadDir { diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index 87fe137d1..71b806d8f 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -142,7 +142,7 @@ impl OpenOptions { node } Err(VfsError::NotFound) => { - if !self.create { + if !self.create && !self.create_new { return ax_err!(NotFound); } fops::create_file(&path)?; @@ -158,7 +158,7 @@ impl OpenOptions { node.truncate(0)?; } // Open - fops::open_file(&path, self.into(), self.append).map(|inner| File { inner }) + fops::open_file(node, self.into(), self.append).map(|inner| File { inner }) } } diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index d41f9e116..1dc720481 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -110,9 +110,9 @@ pub fn remove_file(path: &AbsPath) -> io::Result<()> { /// /// This only works then the new path is in the same mounted fs. pub fn rename(old: &AbsPath, new: &AbsPath) -> io::Result<()> { - let old_node = fops::lookup(old)?; + fops::lookup(old)?; match fops::lookup(new) { - Ok(node) => return ax_err!(AlreadyExists), + Ok(_) => return ax_err!(AlreadyExists), Err(VfsError::NotFound) => fops::rename(old, new), Err(e) => return ax_err!(e), } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 3895a9310..67741df10 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -163,14 +163,8 @@ impl Directory { node.get_attr() } - /// Opens a directory at the path relative to this directory. Returns a - /// [`Directory`] object. - /// - /// This function will not check if the path exists or is a directory, - /// check it with [`get_child_attr`] first. - pub fn open_dir(&self, path: &RelPath, cap: Cap) -> AxResult { - debug!("open dir: {}", path); - let node = self.lookup(path)?; + /// Opens a node as a directory, with permission checked. + pub fn open_dir(&self, node: VfsNodeRef, cap: Cap) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); @@ -179,14 +173,8 @@ impl Directory { Ok(Self::new(node, cap | Cap::EXECUTE)) } - /// Opens a file at the path relative to this directory. Returns a [`File`] - /// object. - /// - /// This function will not check if the path exists or is a file, check it - /// with [`get_child_attr`] first. - pub fn open_file(&self, path: &RelPath, cap: Cap, append: bool) -> AxResult { - debug!("open file: {}", path); - let node = self.lookup(path)?; + /// Opens a node as a file, with permission checked. + pub fn open_file(&self, node: VfsNodeRef, cap: Cap, append: bool) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); @@ -266,13 +254,8 @@ pub fn get_attr(path: &AbsPath) -> AxResult { node.get_attr() } -/// Open a file given an absolute path. -/// -/// This function will not check if the file exists or is a file. -/// Call [`lookup`] to check if first. -pub fn open_file(path: &AbsPath, cap: Cap, append: bool) -> AxResult { - debug!("open file: {}", path); - let node = lookup(path)?; +/// Open a node as a file, with permission checked. +pub fn open_file(node: VfsNodeRef, cap: Cap, append: bool) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); @@ -281,13 +264,8 @@ pub fn open_file(path: &AbsPath, cap: Cap, append: bool) -> AxResult { Ok(File::new(node, cap, append)) } -/// Open a directory given an absolute path. -/// -/// This function will not check if the path exists or is a directory, -/// check it with [`get_attr`] first. -pub fn open_dir(path: &AbsPath, cap: Cap) -> AxResult { - debug!("open dir: {}", path); - let node = lookup(path)?; +/// Open a node as a directory, with permission checked. +pub fn open_dir(node: VfsNodeRef, cap: Cap) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); From 64fbfe7dd6c2439f8579909e060e29ce62550a95 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 31 Oct 2024 15:48:40 +0800 Subject: [PATCH 11/36] fix: root directory lookup path --- crates/axfs_vfs/src/path.rs | 13 +++++++++---- modules/ruxfs/src/fops.rs | 4 ++-- modules/ruxfs/src/root.rs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index 6245d9557..d798875d0 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -75,7 +75,7 @@ impl<'a> AbsPath<'a> { } /// Simply wrap a string into a `AbsPath`. - pub fn new_owned(path: String) -> Self { + pub const fn new_owned(path: String) -> Self { Self(Cow::Owned(path)) } @@ -128,14 +128,19 @@ impl core::fmt::Display for AbsPath<'_> { pub struct RelPath<'a>(Cow<'a, str>); impl<'a> RelPath<'a> { - /// Wrap a string into a `RelPath`. - pub fn new(path: &'a str) -> Self { + /// Simply wrap a string into a `RelPath`. + pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } + /// Wrap a string into a `RelPath` with possibly leading '/' trimmed. + pub fn new_trimmed(path: &'a str) -> Self { + Self(Cow::Borrowed(path.trim_start_matches('/'))) + } + /// Parse and canonicalize a relative path from a string. pub fn new_canonicalized(path: &str) -> Self { - Self(Cow::Owned(canonicalize(path.trim_start_matches("/")))) + Self(Cow::Owned(canonicalize(path.trim_start_matches('/')))) } } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 67741df10..303063312 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -11,7 +11,7 @@ use axerrno::{ax_err, ax_err_type, AxResult}; use axfs_vfs::path::{AbsPath, RelPath}; -use axfs_vfs::{VfsNodeRef, VfsNodeType, VfsNodeOps}; +use axfs_vfs::{VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axio::SeekFrom; use capability::{Cap, WithCap}; @@ -135,7 +135,7 @@ impl File { impl Directory { /// Access the underlying `VfsNode` fn access_node(&self) -> AxResult<&VfsNodeRef> { - self.node.access(Cap::EXECUTE).or(ax_err!(PermissionDenied)) + self.node.access(Cap::EXECUTE).map_err(|_| VfsError::PermissionDenied) } /// Creates an opened directory. diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index a63219af6..83fa920db 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -111,7 +111,7 @@ impl RootDirectory { if max_len == 0 { f(self.main_fs.clone(), path) // not matched any mount point } else { - f(self.mounts[idx].fs.clone(), &RelPath::new(&path[max_len..])) // matched at `idx` + f(self.mounts[idx].fs.clone(), &RelPath::new_trimmed(&path[max_len..])) // matched at `idx` } } } From fb9becced3158d792f6913f240cecc4c96021c7e Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 31 Oct 2024 15:49:16 +0800 Subject: [PATCH 12/36] feat: rmdir mkdirat unlink unlinkat api --- api/ruxos_posix_api/src/imp/fs.rs | 103 +++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 09eaf1409..c7abe683f 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -7,8 +7,8 @@ * See the Mulan PSL v2 for more details. */ -use alloc::{sync::Arc, string::ToString}; -use core::ffi::{c_char, c_int, c_long, c_void, CStr}; +use alloc::{string::ToString, sync::Arc}; +use core::{ffi::{c_char, c_int, c_long, c_void, CStr}, str}; use axerrno::{LinuxError, LinuxResult}; use axio::{Error, PollState, SeekFrom}; @@ -596,7 +596,29 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { syscall_body!(sys_rmdir, { let path = char_ptr_to_path(pathname)?; debug!("sys_rmdir <= path: {:?}", path); - fops::remove_dir(&path.to_abs())?; + match fops::lookup(&path.to_abs()) { + Ok(node) => { + let attr = node.get_attr()?; + if !attr.is_dir() { + return Err(LinuxError::ENOTDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + let mut buf = [ + DirEntry::default(), + DirEntry::default(), + DirEntry::default(), + ]; + if let Ok(n) = node.read_dir(0, &mut buf) { + if n > 2 { + return Err(LinuxError::ENOTEMPTY); + } + } + fops::remove_dir(&path.to_abs())?; + } + Err(e) => return Err(e.into()), + } Ok(0) }) } @@ -606,7 +628,19 @@ pub fn sys_unlink(pathname: *const c_char) -> c_int { syscall_body!(sys_unlink, { let path = char_ptr_to_path(pathname)?; debug!("sys_unlink <= path: {:?}", path); - fops::remove_file(&path.to_abs())?; + match fops::lookup(&path.to_abs()) { + Ok(node) => { + let attr = node.get_attr()?; + if attr.is_dir() { + return Err(LinuxError::EISDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + fops::remove_file(&path.to_abs())?; + } + Err(e) => return Err(e.into()), + } Ok(0) }) } @@ -622,7 +656,36 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { if flags as u32 & ctypes::AT_REMOVEDIR != 0 { return sys_rmdir(pathname); } - sys_unlink(pathname) + syscall_body!(sys_unlinkat, { + let path = char_ptr_to_path(pathname)?; + let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; + let node = if absolute { + fops::lookup(&path.to_abs()) + } else { + Directory::from_fd(fd)?.inner.lock().lookup(&path.to_rel()) + }; + match node { + Ok(node) => { + let attr = node.get_attr()?; + if attr.is_dir() { + return Err(LinuxError::EISDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + if absolute { + fops::remove_file(&path.to_abs())?; + } else { + Directory::from_fd(fd)? + .inner + .lock() + .remove(&path.to_rel())?; + } + } + Err(e) => return Err(e.into()), + } + Ok(0) + }) } /// Creates a new, empty directory at the provided path. @@ -642,8 +705,6 @@ pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { } /// attempts to create a directory named pathname under directory pointed by `fd` -/// -/// TODO: currently fd is not used pub fn sys_mkdirat(fd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> c_int { debug!( "sys_mkdirat <= fd: {}, pathname: {:?}, mode: {:x?}", @@ -651,7 +712,30 @@ pub fn sys_mkdirat(fd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> char_ptr_to_path(pathname), mode ); - sys_mkdir(pathname, mode) + syscall_body!(sys_mkdirat, { + let path = char_ptr_to_path(pathname)?; + let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; + let node = if absolute { + fops::lookup(&path.to_abs()) + } else { + Directory::from_fd(fd)?.inner.lock().lookup(&path.to_rel()) + }; + match node { + Ok(_) => return Err(LinuxError::EEXIST), + Err(Error::NotFound) => { + if absolute { + fops::create_dir(&path.to_abs())?; + } else { + Directory::from_fd(fd)? + .inner + .lock() + .create_dir(&path.to_rel())?; + } + } + Err(e) => return Err(e.into()), + } + Ok(0) + }) } /// Changes the ownership of the file referred to by the open file descriptor fd @@ -718,8 +802,9 @@ pub unsafe fn sys_getdents64(fd: c_int, dirp: *mut LinuxDirent64, count: ctypes: let mut entry = [DirEntry::default()]; let offset = dir.inner.lock().entry_idx(); let n = dir.inner.lock().read_dir(&mut entry)?; + debug!("entry {:?}", str::from_utf8(entry[0].name_as_bytes()).unwrap()); if n == 0 { - return Ok(0); + return Ok(written as isize); } let entry = &entry[0]; From fa5ee1f900cd8914a29a899730fb1f5dced3da47 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 6 Nov 2024 22:39:39 +0800 Subject: [PATCH 13/36] fix: unlinkat with removedir --- api/ruxos_posix_api/src/imp/fs.rs | 73 +++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index c7abe683f..5a60770ce 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -8,7 +8,10 @@ */ use alloc::{string::ToString, sync::Arc}; -use core::{ffi::{c_char, c_int, c_long, c_void, CStr}, str}; +use core::{ + ffi::{c_char, c_int, c_long, c_void, CStr}, + str, +}; use axerrno::{LinuxError, LinuxResult}; use axio::{Error, PollState, SeekFrom}; @@ -195,6 +198,7 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> let node = match fops::lookup(&path.to_abs()) { Ok(node) => { if flags & ctypes::O_EXCL != 0 { + debug!("exist {} {}", flags, ctypes::O_EXCL); return Err(LinuxError::EEXIST); } node @@ -249,6 +253,7 @@ pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mo let attr = node.get_attr()?; // Node exists but O_EXCL is set if flags & ctypes::O_EXCL != 0 { + debug!("exist {} {}", flags, ctypes::O_EXCL); return Err(LinuxError::EEXIST); } // Node is not a directory but O_DIRECTORY is set @@ -605,6 +610,8 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { if !attr.perm().owner_writable() { return Err(LinuxError::EPERM); } + // Directory has no check_empty() method, so + // we uses a brute force way to check it. let mut buf = [ DirEntry::default(), DirEntry::default(), @@ -653,9 +660,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { char_ptr_to_path(pathname), flags ); - if flags as u32 & ctypes::AT_REMOVEDIR != 0 { - return sys_rmdir(pathname); - } + let rmdir = flags as u32 & ctypes::AT_REMOVEDIR != 0; syscall_body!(sys_unlinkat, { let path = char_ptr_to_path(pathname)?; let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; @@ -667,19 +672,48 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { match node { Ok(node) => { let attr = node.get_attr()?; - if attr.is_dir() { - return Err(LinuxError::EISDIR); - } - if !attr.perm().owner_writable() { - return Err(LinuxError::EPERM); - } - if absolute { - fops::remove_file(&path.to_abs())?; + if rmdir { + if !attr.is_dir() { + return Err(LinuxError::ENOTDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + // Directory has no check_empty() method, so + // we uses a brute force way to check it. + let mut buf = [ + DirEntry::default(), + DirEntry::default(), + DirEntry::default(), + ]; + if let Ok(n) = node.read_dir(0, &mut buf) { + if n > 2 { + return Err(LinuxError::ENOTEMPTY); + } + } + if absolute { + fops::remove_dir(&path.to_abs())?; + } else { + Directory::from_fd(fd)? + .inner + .lock() + .remove(&path.to_rel())?; + } } else { - Directory::from_fd(fd)? - .inner - .lock() - .remove(&path.to_rel())?; + if attr.is_dir() { + return Err(LinuxError::EISDIR); + } + if !attr.perm().owner_writable() { + return Err(LinuxError::EPERM); + } + if absolute { + fops::remove_file(&path.to_abs())?; + } else { + Directory::from_fd(fd)? + .inner + .lock() + .remove(&path.to_rel())?; + } } } Err(e) => return Err(e.into()), @@ -802,7 +836,10 @@ pub unsafe fn sys_getdents64(fd: c_int, dirp: *mut LinuxDirent64, count: ctypes: let mut entry = [DirEntry::default()]; let offset = dir.inner.lock().entry_idx(); let n = dir.inner.lock().read_dir(&mut entry)?; - debug!("entry {:?}", str::from_utf8(entry[0].name_as_bytes()).unwrap()); + debug!( + "entry {:?}", + str::from_utf8(entry[0].name_as_bytes()).unwrap() + ); if n == 0 { return Ok(written as isize); } @@ -941,7 +978,7 @@ impl core::fmt::Display for Path<'_> { } /// from char_ptr get path_str -pub fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { +fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { if ptr.is_null() { return Err(LinuxError::EFAULT); } From 0a452f9dfe3e5ff6be081554bafcf2bded9df1e8 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 7 Nov 2024 11:18:15 +0800 Subject: [PATCH 14/36] test: use specific test dir --- apps/custom/harness/src/main.rs | 7 +++++++ modules/ruxfs/src/fops.rs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/custom/harness/src/main.rs b/apps/custom/harness/src/main.rs index 0365d4a3a..1fd1486f2 100644 --- a/apps/custom/harness/src/main.rs +++ b/apps/custom/harness/src/main.rs @@ -129,6 +129,13 @@ fn main() { println!("CMD_BUF at {:p}", unsafe { CMD_BUF.as_ptr() }); println!("RETV_BUF at {:p}", unsafe { RETV_BUF.as_ptr() }); println!("OUTPUT_BUF at {:p}", unsafe { OUTPUT_BUF.as_ptr() }); + + // Make test directory. + let test_dir = b"test\0" as *const _ as *const i8; + sys_mkdirat(-100, test_dir, 0o755); + // Chdir to test directory. + sys_chdir(test_dir); + let mut harness = Harness::::new( unsafe { MemPort::new(&CMD_BUF, &mut RETV_BUF, &mut OUTPUT_BUF) }, FsSyscallExecutor, diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 303063312..0f80fbf4d 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -339,9 +339,9 @@ pub fn set_current_dir(path: AbsPath<'static>) -> AxResult { let node = lookup(&path)?; let attr = node.get_attr()?; if !attr.is_dir() { - ax_err!(NotADirectory) + Err(VfsError::NotADirectory) } else if !attr.perm().owner_executable() { - ax_err!(PermissionDenied) + Err(VfsError::PermissionDenied) } else { *CURRENT_DIR.lock() = node; *CURRENT_DIR_PATH.lock() = path; From 3bf32a5e0214b053795f436f6359a65aa6777010 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 14 Nov 2024 14:53:24 +0800 Subject: [PATCH 15/36] feat: support fatfs and ext4 fs --- Cargo.lock | 29 ++ api/ruxfeat/Cargo.toml | 4 + apps/custom/harness/src/main.rs | 11 +- modules/ruxfs/Cargo.toml | 6 + modules/ruxfs/src/dev.rs | 24 ++ modules/ruxfs/src/fs/another_ext4.rs | 281 ++++++++++++++++++ modules/ruxfs/src/fs/ext4_rs.rs | 410 +++++++++++++++++++++++++++ modules/ruxfs/src/fs/lwext4_rust.rs | 408 ++++++++++++++++++++++++++ modules/ruxfs/src/fs/mod.rs | 6 + modules/ruxfs/src/lib.rs | 21 ++ ulib/axstd/Cargo.toml | 4 + 11 files changed, 1198 insertions(+), 6 deletions(-) create mode 100644 modules/ruxfs/src/fs/another_ext4.rs create mode 100644 modules/ruxfs/src/fs/ext4_rs.rs create mode 100644 modules/ruxfs/src/fs/lwext4_rust.rs diff --git a/Cargo.lock b/Cargo.lock index 06acda278..78c904335 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "another_ext4" +version = "0.1.0" +source = "git+https://github.com/LJxTHUCS/another_ext4.git#282418b4b197472acaae60469b608499ef1161d2" +dependencies = [ + "bitflags 2.6.0", + "log", +] + [[package]] name = "anstyle" version = "1.0.8" @@ -777,6 +786,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ext4_rs" +version = "1.0.0" +source = "git+https://github.com/yuoo655/ext4_rs.git?rev=6bcc7f5#6bcc7f5d6382ba1940f29f9a15869f35fe77b5c0" +dependencies = [ + "bitflags 2.6.0", + "log", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1125,6 +1143,14 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lwext4_rust" +version = "0.2.0" +dependencies = [ + "log", + "printf-compat", +] + [[package]] name = "lwip_rust" version = "0.1.0" @@ -1666,6 +1692,7 @@ dependencies = [ name = "ruxfs" version = "0.1.0" dependencies = [ + "another_ext4", "axalloc", "axerrno", "axfs_devfs", @@ -1677,9 +1704,11 @@ dependencies = [ "cfg-if", "crate_interface", "driver_block", + "ext4_rs", "fatfs", "lazy_init", "log", + "lwext4_rust", "memory_addr", "ruxdriver", "ruxtask", diff --git a/api/ruxfeat/Cargo.toml b/api/ruxfeat/Cargo.toml index 7926cee2f..c22522907 100644 --- a/api/ruxfeat/Cargo.toml +++ b/api/ruxfeat/Cargo.toml @@ -50,6 +50,10 @@ fs = ["alloc", "dep:ruxfs", "ruxruntime/fs"] blkfs = ["ruxdriver/virtio-blk", "ruxruntime/blkfs"] myfs = ["ruxfs?/myfs"] 9pfs = [] +fatfs = ["ruxfs?/fatfs"] +lwext4_rust = ["ruxfs?/lwext4_rust"] +ext4_rs = ["ruxfs?/ext4_rs"] +another_ext4 = ["ruxfs?/another_ext4"] # Networking net = ["alloc", "ruxdriver/virtio-net", "dep:ruxnet", "ruxruntime/net"] diff --git a/apps/custom/harness/src/main.rs b/apps/custom/harness/src/main.rs index 1fd1486f2..52c6131b4 100644 --- a/apps/custom/harness/src/main.rs +++ b/apps/custom/harness/src/main.rs @@ -1,14 +1,13 @@ #![no_std] #![no_main] -use axstd::println; -use core::{clone::Clone, format_args, panic}; +use core::{clone::Clone, panic}; use core::{ffi::c_void, mem::size_of}; use km_command::{fs::LibcDirent, FromBytes}; use km_harness::{executor, harness_command, Command, Harness, MemPort}; use ruxos_posix_api::{ ctypes::dirent, sys_chdir, sys_close, sys_dup, sys_fstat, sys_getcwd, sys_getdents64, - sys_mkdirat, sys_openat, sys_unlinkat, + sys_mkdirat, sys_openat, sys_unlinkat }; /// Size of harness buffer. @@ -126,9 +125,9 @@ executor!( #[no_mangle] fn main() { - println!("CMD_BUF at {:p}", unsafe { CMD_BUF.as_ptr() }); - println!("RETV_BUF at {:p}", unsafe { RETV_BUF.as_ptr() }); - println!("OUTPUT_BUF at {:p}", unsafe { OUTPUT_BUF.as_ptr() }); + log::warn!("CMD_BUF at {:p}", unsafe { CMD_BUF.as_ptr() }); + log::warn!("RETV_BUF at {:p}", unsafe { RETV_BUF.as_ptr() }); + log::warn!("OUTPUT_BUF at {:p}", unsafe { OUTPUT_BUF.as_ptr() }); // Make test directory. let test_dir = b"test\0" as *const _ as *const i8; diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 6c15e6c64..9f53dc2c2 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -18,6 +18,9 @@ procfs = ["dep:axfs_ramfs"] sysfs = ["dep:axfs_ramfs"] etcfs = ["dep:axfs_ramfs"] fatfs = ["dep:fatfs"] +lwext4_rust = ["dep:lwext4_rust"] +ext4_rs = ["dep:ext4_rs"] +another_ext4 = ["dep:another_ext4"] myfs = ["dep:crate_interface"] use-ramdisk = [] alloc = ["axalloc"] @@ -41,6 +44,9 @@ axsync = { path = "../axsync" } crate_interface = { version = "0.1.1", optional = true } axalloc = { path = "../axalloc", optional = true } memory_addr = "0.1.0" +lwext4_rust = { path = "../../crates/lwext4_rust", optional = true } +ext4_rs = { git = "https://github.com/yuoo655/ext4_rs.git", rev= "6bcc7f5", optional = true } +another_ext4 = { git = "https://github.com/LJxTHUCS/another_ext4.git", optional = true } [dependencies.fatfs] git = "https://github.com/syswonder/rust-fatfs.git" diff --git a/modules/ruxfs/src/dev.rs b/modules/ruxfs/src/dev.rs index f897c4c76..3ed97fdaf 100644 --- a/modules/ruxfs/src/dev.rs +++ b/modules/ruxfs/src/dev.rs @@ -99,6 +99,30 @@ impl Disk { Ok(write_size) } + /// Read a single block starting from the specified offset. + #[allow(unused)] + pub fn read_offset(&mut self, offset: usize) -> [u8; BLOCK_SIZE] { + let block_id = offset / BLOCK_SIZE; + let mut block_data = [0u8; BLOCK_SIZE]; + self.dev + .read_block(block_id as u64, &mut block_data) + .unwrap(); + block_data + } + + /// Write single block starting from the specified offset. + #[allow(unused)] + pub fn write_offset(&mut self, offset: usize, buf: &[u8]) -> DevResult { + assert!( + buf.len() == BLOCK_SIZE, + "Buffer length must be equal to BLOCK_SIZE" + ); + assert!(offset % BLOCK_SIZE == 0); + let block_id = offset / BLOCK_SIZE; + self.dev.write_block(block_id as u64, buf).unwrap(); + Ok(buf.len()) + } + ///flush device cache pub fn do_flush(&mut self) -> DevResult { self.dev.flush() diff --git a/modules/ruxfs/src/fs/another_ext4.rs b/modules/ruxfs/src/fs/another_ext4.rs new file mode 100644 index 000000000..4a41ab7bf --- /dev/null +++ b/modules/ruxfs/src/fs/another_ext4.rs @@ -0,0 +1,281 @@ +use crate::dev::Disk; +use alloc::sync::Arc; +use another_ext4::{ + Block, BlockDevice, ErrCode as Ext4ErrorCode, Ext4, Ext4Error, FileType as EXt4FileType, + InodeMode as Ext4InodeMode, BLOCK_SIZE as EXT4_BLOCK_SIZE, EXT4_ROOT_INO, +}; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; + +pub struct DiskAdapter(Arc>); + +unsafe impl Send for DiskAdapter {} +unsafe impl Sync for DiskAdapter {} + +// The io block size of the disk layer +const DISK_BLOCK_SIZE: usize = 512; + +// The block size of the file system +pub const BLOCK_SIZE: usize = EXT4_BLOCK_SIZE; + +impl BlockDevice for DiskAdapter { + fn read_block(&self, block_id: u64) -> Block { + let mut disk = self.0.lock(); + let base = block_id as usize * EXT4_BLOCK_SIZE; + let mut data = [0u8; EXT4_BLOCK_SIZE]; + // Per-disk-block read + for i in 0..(EXT4_BLOCK_SIZE / DISK_BLOCK_SIZE) { + let dblock = disk.read_offset(base + i * DISK_BLOCK_SIZE); + data[i * DISK_BLOCK_SIZE..(i + 1) * DISK_BLOCK_SIZE].copy_from_slice(&dblock); + } + Block::new(block_id, data) + } + + fn write_block(&self, block: &Block) { + let mut disk = self.0.lock(); + let base = block.id as usize * EXT4_BLOCK_SIZE; + // Per-disk-block write + for i in 0..(EXT4_BLOCK_SIZE / DISK_BLOCK_SIZE) { + let dblock = &block.data[i * DISK_BLOCK_SIZE..(i + 1) * DISK_BLOCK_SIZE]; + let _ = disk.write_offset(base + i * DISK_BLOCK_SIZE, dblock); + } + } +} + +pub struct Ext4FileSystem(Arc); + +impl Ext4FileSystem { + pub fn new(disk: Disk) -> Self { + let block_device = Arc::new(DiskAdapter(Arc::new(Mutex::new(disk)))); + let ext4 = Ext4::load(block_device).expect("Failed to load ext4 filesystem"); + log::info!("Ext4 filesystem loaded"); + Self(Arc::new(ext4)) + } +} + +impl VfsOps for Ext4FileSystem { + fn root_dir(&self) -> VfsNodeRef { + Arc::new(Ext4VirtInode::new(EXT4_ROOT_INO, self.0.clone())) + } + fn umount(&self) -> VfsResult { + self.0.flush_all(); + Ok(()) + } +} + +pub struct Ext4VirtInode { + id: u32, + fs: Arc, +} + +unsafe impl Send for Ext4VirtInode {} +unsafe impl Sync for Ext4VirtInode {} + +impl Ext4VirtInode { + fn new(id: u32, fs: Arc) -> Self { + log::trace!("Create Ext4VirtInode {}", id); + Self { id, fs } + } +} + +impl VfsNodeOps for Ext4VirtInode { + fn open(&self) -> VfsResult { + Ok(()) + } + + fn release(&self) -> VfsResult { + Ok(()) + } + + fn get_attr(&self) -> VfsResult { + self.fs + .getattr(self.id) + .map(|attr| { + VfsNodeAttr::new( + map_perm(attr.perm), + map_type(attr.ftype), + attr.size, + attr.blocks, + ) + }) + .map_err(map_error) + } + + // file operations: + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + self.fs + .read(self.id, offset as usize, buf) + .map_err(map_error) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + self.fs + .write(self.id, offset as usize, buf) + .map_err(map_error) + } + + fn fsync(&self) -> VfsResult { + Ok(()) + } + + fn truncate(&self, size: u64) -> VfsResult { + // TODO: Simple implementation, just set the size, + // not truncate the file in the disk + self.fs + .setattr( + self.id, + None, + None, + None, + Some(size), + None, + None, + None, + None, + ) + .map_err(map_error) + } + + // directory operations: + + fn parent(&self) -> Option { + self.fs.lookup(self.id, "..").map_or(None, |parent| { + Some(Arc::new(Ext4VirtInode::new(parent, self.fs.clone()))) + }) + } + + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + match self.fs.generic_lookup(self.id, path) { + Ok(id) => Ok(Arc::new(Ext4VirtInode::new(id, self.fs.clone()))), + Err(e) => Err(map_error(e)), + } + } + + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + if self.fs.generic_lookup(self.id, path).is_ok() { + return Ok(()); + } + let mode = Ext4InodeMode::from_type_and_perm(map_type_inv(ty), Ext4InodeMode::ALL_RWX); + self.fs + .generic_create(self.id, path, mode) + .map(|_| ()) + .map_err(map_error) + } + + fn remove(&self, path: &RelPath) -> VfsResult { + self.fs.unlink(self.id, path).map_err(map_error) + } + + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + self.fs + .listdir(self.id) + .map(|entries| { + for (i, entry) in entries.iter().skip(start_idx).enumerate() { + if i >= dirents.len() { + return i; + } + dirents[i] = VfsDirEntry::new(&entry.name(), map_type(entry.file_type())); + } + entries.len() - start_idx + }) + .map_err(map_error) + } + + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + self.fs + .generic_rename(self.id, src_path, dst_path) + .map_err(map_error) + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +fn map_error(ext4_err: Ext4Error) -> VfsError { + log::warn!("Ext4 error: {:?}", ext4_err); + match ext4_err.code() { + Ext4ErrorCode::EPERM => VfsError::PermissionDenied, + Ext4ErrorCode::ENOENT => VfsError::NotFound, + Ext4ErrorCode::EIO => VfsError::Io, + Ext4ErrorCode::ENXIO => VfsError::Io, // ? + Ext4ErrorCode::E2BIG => VfsError::InvalidInput, + Ext4ErrorCode::ENOMEM => VfsError::NoMemory, + Ext4ErrorCode::EACCES => VfsError::PermissionDenied, // ? + Ext4ErrorCode::EFAULT => VfsError::BadAddress, + Ext4ErrorCode::EEXIST => VfsError::AlreadyExists, + Ext4ErrorCode::ENODEV => VfsError::Io, // ? + Ext4ErrorCode::ENOTDIR => VfsError::NotADirectory, + Ext4ErrorCode::EISDIR => VfsError::IsADirectory, + Ext4ErrorCode::EINVAL => VfsError::InvalidData, + Ext4ErrorCode::EFBIG => VfsError::InvalidData, + Ext4ErrorCode::ENOSPC => VfsError::StorageFull, + Ext4ErrorCode::EROFS => VfsError::PermissionDenied, + Ext4ErrorCode::EMLINK => VfsError::Io, // ? + Ext4ErrorCode::ERANGE => VfsError::InvalidData, + Ext4ErrorCode::ENOTEMPTY => VfsError::DirectoryNotEmpty, + Ext4ErrorCode::ENODATA => VfsError::NotFound, // `NotFound` only for entry? + Ext4ErrorCode::ENOTSUP => VfsError::Io, // ? + Ext4ErrorCode::ELINKFAIL => VfsError::Io, // ? + Ext4ErrorCode::EALLOCFAIL => VfsError::StorageFull, // ? + } +} + +fn map_type(ext4_type: EXt4FileType) -> VfsNodeType { + match ext4_type { + EXt4FileType::RegularFile => VfsNodeType::File, + EXt4FileType::Directory => VfsNodeType::Dir, + EXt4FileType::CharacterDev => VfsNodeType::CharDevice, + EXt4FileType::BlockDev => VfsNodeType::BlockDevice, + EXt4FileType::Fifo => VfsNodeType::Fifo, + EXt4FileType::Socket => VfsNodeType::Socket, + EXt4FileType::SymLink => VfsNodeType::SymLink, + EXt4FileType::Unknown => VfsNodeType::File, + } +} + +fn map_type_inv(vfs_type: VfsNodeType) -> EXt4FileType { + match vfs_type { + VfsNodeType::File => EXt4FileType::RegularFile, + VfsNodeType::Dir => EXt4FileType::Directory, + VfsNodeType::CharDevice => EXt4FileType::CharacterDev, + VfsNodeType::BlockDevice => EXt4FileType::BlockDev, + VfsNodeType::Fifo => EXt4FileType::Fifo, + VfsNodeType::Socket => EXt4FileType::Socket, + VfsNodeType::SymLink => EXt4FileType::SymLink, + } +} + +fn map_perm(perm: Ext4InodeMode) -> VfsNodePerm { + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + if perm.contains(Ext4InodeMode::USER_READ) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + if perm.contains(Ext4InodeMode::USER_WRITE) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(Ext4InodeMode::USER_EXEC) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(Ext4InodeMode::GROUP_READ) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + if perm.contains(Ext4InodeMode::GROUP_WRITE) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(Ext4InodeMode::GROUP_EXEC) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(Ext4InodeMode::OTHER_READ) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + if perm.contains(Ext4InodeMode::OTHER_WRITE) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(Ext4InodeMode::OTHER_EXEC) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + vfs_perm +} \ No newline at end of file diff --git a/modules/ruxfs/src/fs/ext4_rs.rs b/modules/ruxfs/src/fs/ext4_rs.rs new file mode 100644 index 000000000..9ff6b1614 --- /dev/null +++ b/modules/ruxfs/src/fs/ext4_rs.rs @@ -0,0 +1,410 @@ +use crate::dev::Disk; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::*; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; +use core::cell::RefCell; +use ext4_rs::*; + +pub struct DiskAdapter { + inner: RefCell, +} + +unsafe impl Send for DiskAdapter {} +unsafe impl Sync for DiskAdapter {} + +// The io block size of the disk layer +const DISK_BLOCK_SIZE: usize = 512; + +// The block size of the file system +pub const BLOCK_SIZE: usize = 4096; + +impl BlockDevice for DiskAdapter { + fn read_offset(&self, offset: usize) -> Vec { + let mut disk = self.inner.borrow_mut(); + let mut buf = vec![0u8; BLOCK_SIZE]; + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + let mut total_bytes_read = 0; + + while total_bytes_read < buf.len() { + let current_block_id = start_block_id + (total_bytes_read / DISK_BLOCK_SIZE); + let bytes_to_copy = + (buf.len() - total_bytes_read).min(DISK_BLOCK_SIZE - offset_in_block); + + let block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE + offset_in_block); + + buf[total_bytes_read..total_bytes_read + bytes_to_copy] + .copy_from_slice(&block_data[offset_in_block..offset_in_block + bytes_to_copy]); + + total_bytes_read += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks read from the beginning + } + + buf + } + + fn write_offset(&self, offset: usize, buf: &[u8]) { + let mut disk = self.inner.borrow_mut(); + + let start_block_id = offset / DISK_BLOCK_SIZE; + let mut offset_in_block = offset % DISK_BLOCK_SIZE; + + let bytes_to_write = buf.len(); + let mut total_bytes_written = 0; + + while total_bytes_written < bytes_to_write { + let current_block_id = start_block_id + (total_bytes_written / DISK_BLOCK_SIZE); + let bytes_to_copy = + (bytes_to_write - total_bytes_written).min(DISK_BLOCK_SIZE - offset_in_block); + + let mut block_data = disk.read_offset(current_block_id * DISK_BLOCK_SIZE); + + block_data[offset_in_block..offset_in_block + bytes_to_copy] + .copy_from_slice(&buf[total_bytes_written..total_bytes_written + bytes_to_copy]); + + disk.write_offset(current_block_id * DISK_BLOCK_SIZE, &block_data) + .unwrap(); + + total_bytes_written += bytes_to_copy; + offset_in_block = 0; // After the first block, subsequent blocks start at the beginning + } + } +} + +pub struct Ext4FileSystem { + #[allow(unused)] + inner: Arc, + root_dir: VfsNodeRef, +} + +impl Ext4FileSystem { + pub fn new(disk: Disk) -> Self { + let block_device = Arc::new(DiskAdapter { + inner: RefCell::new(disk), + }); + let inner = Ext4::open(block_device); + let root = Arc::new(Ext4FileWrapper::new(inner.clone())); + Self { + inner: inner.clone(), + root_dir: root, + } + } +} + +impl VfsOps for Ext4FileSystem { + fn root_dir(&self) -> VfsNodeRef { + Arc::clone(&self.root_dir) + } + + fn umount(&self) -> VfsResult { + log::info!("umount:"); + // todo!() + Ok(()) + } +} + +pub struct Ext4FileWrapper { + ext4_file: Mutex, + ext4: Arc, +} + +unsafe impl Send for Ext4FileWrapper {} +unsafe impl Sync for Ext4FileWrapper {} + +impl Ext4FileWrapper { + fn new(ext4: Arc) -> Self { + Self { + ext4_file: Mutex::new(Ext4File::new()), + ext4: ext4, + } + } +} + +impl VfsNodeOps for Ext4FileWrapper { + /// Do something when the node is opened. + fn open(&self) -> VfsResult { + // log::info!("opening file"); + // let mut ext4_file = self.ext4_file.lock(); + // let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + Ok(()) + } + + /// Do something when the node is closed. + fn release(&self) -> VfsResult { + Ok(()) + } + + /// Get the attributes of the node. + fn get_attr(&self) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + let root_inode_ref = + Ext4InodeRef::get_inode_ref(Arc::downgrade(&self.ext4).clone(), ext4_file.inode); + let inode_mode = root_inode_ref.inner.inode.mode; + let size = ext4_file.fsize; + // BLOCK_SIZE / DISK_BLOCK_SIZE + let blocks = root_inode_ref.inner.inode.blocks * 8; + let (ty, perm) = map_imode(inode_mode as u16); + drop(ext4_file); + Ok(VfsNodeAttr::new(perm, ty, size as _, blocks as _)) + } + + // file operations: + + /// Read data from the file at the given offset. + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let read_len = buf.len(); + let mut read_cnt = 0; + + let r = self + .ext4 + .ext4_file_read(&mut ext4_file, buf, read_len, &mut read_cnt); + + if let Err(e) = r { + match e.error() { + Errnum::EINVAL => { + drop(ext4_file); + Ok(0) + } + _ => { + drop(ext4_file); + Err(VfsError::InvalidInput) + } + } + } else { + drop(ext4_file); + Ok(read_len) + } + } + + /// Write data to the file at the given offset. + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + ext4_file.fpos = offset as usize; + + let write_size = buf.len(); + + self.ext4.ext4_file_write(&mut ext4_file, &buf, write_size); + + Ok(write_size) + } + + /// Flush the file, synchronize the data to disk. + fn fsync(&self) -> VfsResult { + todo!() + } + + /// Truncate the file to the given size. + fn truncate(&self, _size: u64) -> VfsResult { + todo!() + } + + // directory operations: + + /// Get the parent directory of this directory. + /// + /// Return `None` if the node is a file. + fn parent(&self) -> Option { + None + } + + /// Lookup the node with given `path` in the directory. + /// + /// Return the node if found. + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + let mut ext4_file = self.ext4_file.lock(); + let r = self.ext4.ext4_open(&mut ext4_file, path, "r+", false); + + if let Err(e) = r { + match e.error() { + Errnum::ENOENT => Err(VfsError::NotFound), + Errnum::EALLOCFIAL => Err(VfsError::InvalidInput), + Errnum::ELINKFIAL => Err(VfsError::InvalidInput), + + _ => Err(VfsError::InvalidInput), + } + } else { + drop(ext4_file); + // log::error!("file found"); + Ok(self.clone()) + } + } + + /// Create a new node with the given `path` in the directory + /// + /// Return [`Ok(())`](Ok) if it already exists. + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + let types = match ty { + VfsNodeType::Fifo => DirEntryType::EXT4_DE_FIFO, + VfsNodeType::CharDevice => DirEntryType::EXT4_DE_CHRDEV, + VfsNodeType::Dir => DirEntryType::EXT4_DE_DIR, + VfsNodeType::BlockDevice => DirEntryType::EXT4_DE_BLKDEV, + VfsNodeType::File => DirEntryType::EXT4_DE_REG_FILE, + VfsNodeType::SymLink => DirEntryType::EXT4_DE_SYMLINK, + VfsNodeType::Socket => DirEntryType::EXT4_DE_SOCK, + }; + + let mut ext4file = self.ext4_file.lock(); + + if types == DirEntryType::EXT4_DE_DIR { + let _ = self.ext4.ext4_dir_mk(path); + } else { + let _ = self.ext4.ext4_open(&mut ext4file, path, "w+", true); + } + + drop(ext4file); + + Ok(()) + } + + /// Remove the node with the given `path` in the directory. + fn remove(&self, _path: &RelPath) -> VfsResult { + todo!() + } + + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + let ext4_file = self.ext4_file.lock(); + let inode_num = ext4_file.inode; + let entries: Vec = self.ext4.read_dir_entry(inode_num as _); + let mut iter = entries.into_iter().skip(start_idx); + + for (i, out_entry) in dirents.iter_mut().enumerate() { + let x: Option = iter.next(); + match x { + Some(ext4direntry) => { + let name = ext4direntry.name; + let name_len = ext4direntry.name_len; + let file_type = unsafe { ext4direntry.inner.inode_type }; + let (ty, _) = map_dir_imode(file_type as u16); + let name = get_name(name, name_len as usize).unwrap(); + *out_entry = VfsDirEntry::new(name.as_str(), ty); + } + _ => return Ok(i), + } + } + + drop(ext4_file); + Ok(dirents.len()) + } + + /// Renames or moves existing file or directory. + fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult { + todo!() + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +fn map_dir_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let diren_type = imode; + let type_code = ext4_rs::DirEntryType::from_bits(diren_type as u8).unwrap(); + let ty = match type_code { + DirEntryType::EXT4_DE_REG_FILE => VfsNodeType::File, + DirEntryType::EXT4_DE_DIR => VfsNodeType::Dir, + DirEntryType::EXT4_DE_CHRDEV => VfsNodeType::CharDevice, + DirEntryType::EXT4_DE_BLKDEV => VfsNodeType::BlockDevice, + DirEntryType::EXT4_DE_FIFO => VfsNodeType::Fifo, + DirEntryType::EXT4_DE_SOCK => VfsNodeType::Socket, + DirEntryType::EXT4_DE_SYMLINK => VfsNodeType::SymLink, + _ => { + // log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} + +fn map_imode(imode: u16) -> (VfsNodeType, VfsNodePerm) { + let file_type = (imode & 0xf000) as usize; + let ty = match file_type { + EXT4_INODE_MODE_FIFO => VfsNodeType::Fifo, + EXT4_INODE_MODE_CHARDEV => VfsNodeType::CharDevice, + EXT4_INODE_MODE_DIRECTORY => VfsNodeType::Dir, + EXT4_INODE_MODE_BLOCKDEV => VfsNodeType::BlockDevice, + EXT4_INODE_MODE_FILE => VfsNodeType::File, + EXT4_INODE_MODE_SOFTLINK => VfsNodeType::SymLink, + EXT4_INODE_MODE_SOCKET => VfsNodeType::Socket, + _ => { + // log::info!("{:x?}", imode); + VfsNodeType::File + } + }; + + let perm = ext4_rs::FileMode::from_bits_truncate(imode); + let mut vfs_perm = VfsNodePerm::from_bits_truncate(0); + + if perm.contains(ext4_rs::FileMode::S_IXOTH) { + vfs_perm |= VfsNodePerm::OTHER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWOTH) { + vfs_perm |= VfsNodePerm::OTHER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IROTH) { + vfs_perm |= VfsNodePerm::OTHER_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXGRP) { + vfs_perm |= VfsNodePerm::GROUP_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWGRP) { + vfs_perm |= VfsNodePerm::GROUP_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRGRP) { + vfs_perm |= VfsNodePerm::GROUP_READ; + } + + if perm.contains(ext4_rs::FileMode::S_IXUSR) { + vfs_perm |= VfsNodePerm::OWNER_EXEC; + } + if perm.contains(ext4_rs::FileMode::S_IWUSR) { + vfs_perm |= VfsNodePerm::OWNER_WRITE; + } + if perm.contains(ext4_rs::FileMode::S_IRUSR) { + vfs_perm |= VfsNodePerm::OWNER_READ; + } + + (ty, vfs_perm) +} \ No newline at end of file diff --git a/modules/ruxfs/src/fs/lwext4_rust.rs b/modules/ruxfs/src/fs/lwext4_rust.rs new file mode 100644 index 000000000..db231d5e5 --- /dev/null +++ b/modules/ruxfs/src/fs/lwext4_rust.rs @@ -0,0 +1,408 @@ +use crate::alloc::string::String; +use alloc::sync::Arc; +use axerrno::AxError; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; +use axsync::Mutex; +use lwext4_rust::bindings::{ + O_CREAT, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, +}; +use lwext4_rust::{Ext4BlockWrapper, Ext4File, InodeTypes, KernelDevOp}; + +use crate::dev::Disk; +pub const BLOCK_SIZE: usize = 512; + +#[allow(dead_code)] +pub struct Ext4FileSystem { + inner: Ext4BlockWrapper, + root: VfsNodeRef, +} + +unsafe impl Sync for Ext4FileSystem {} +unsafe impl Send for Ext4FileSystem {} + +impl Ext4FileSystem { + #[cfg(feature = "use-ramdisk")] + pub fn new(mut disk: Disk) -> Self { + unimplemented!() + } + + #[cfg(not(feature = "use-ramdisk"))] + pub fn new(disk: Disk) -> Self { + info!( + "Got Disk size:{}, position:{}", + disk.size(), + disk.position() + ); + let inner = + Ext4BlockWrapper::::new(disk).expect("failed to initialize EXT4 filesystem"); + let root = Arc::new(FileWrapper::new("/", InodeTypes::EXT4_DE_DIR)); + Self { inner, root } + } +} + +/// The [`VfsOps`] trait provides operations on a filesystem. +impl VfsOps for Ext4FileSystem { + // mount() + + fn root_dir(&self) -> VfsNodeRef { + debug!("Get root_dir"); + //let root_dir = unsafe { (*self.root.get()).as_ref().unwrap() }; + Arc::clone(&self.root) + } +} + +pub struct FileWrapper(Mutex); + +unsafe impl Send for FileWrapper {} +unsafe impl Sync for FileWrapper {} + +impl FileWrapper { + fn new(path: &str, types: InodeTypes) -> Self { + info!("FileWrapper new {:?} {}", types, path); + //file.file_read_test("/test/test.txt", &mut buf); + + Self(Mutex::new(Ext4File::new(path, types))) + } + + fn path_deal_with(&self, path: &str) -> String { + if path.starts_with('/') { + warn!("path_deal_with: {}", path); + } + let p = path.trim_matches('/'); // 首尾去除 + if p.is_empty() || p == "." { + return String::new(); + } + + if let Some(rest) = p.strip_prefix("./") { + //if starts with "./" + return self.path_deal_with(rest); + } + let rest_p = p.replace("//", "/"); + if p != rest_p { + return self.path_deal_with(&rest_p); + } + + //Todo ? ../ + //注:lwext4创建文件必须提供文件path的绝对路径 + let file = self.0.lock(); + let path = file.get_path(); + let fpath = String::from(path.to_str().unwrap().trim_end_matches('/')) + "/" + p; + info!("dealt with full path: {}", fpath.as_str()); + fpath + } +} + +/// The [`VfsNodeOps`] trait provides operations on a file or a directory. +impl VfsNodeOps for FileWrapper { + fn get_attr(&self) -> VfsResult { + let mut file = self.0.lock(); + + let perm = file.file_mode_get().unwrap_or(0o755); + let perm = VfsNodePerm::from_bits_truncate((perm as u16) & 0o777); + + let vtype = file.file_type_get(); + let vtype = match vtype { + InodeTypes::EXT4_INODE_MODE_FIFO => VfsNodeType::Fifo, + InodeTypes::EXT4_INODE_MODE_CHARDEV => VfsNodeType::CharDevice, + InodeTypes::EXT4_INODE_MODE_DIRECTORY => VfsNodeType::Dir, + InodeTypes::EXT4_INODE_MODE_BLOCKDEV => VfsNodeType::BlockDevice, + InodeTypes::EXT4_INODE_MODE_FILE => VfsNodeType::File, + InodeTypes::EXT4_INODE_MODE_SOFTLINK => VfsNodeType::SymLink, + InodeTypes::EXT4_INODE_MODE_SOCKET => VfsNodeType::Socket, + _ => { + warn!("unknown file type: {:?}", vtype); + VfsNodeType::File + } + }; + + let size = if vtype == VfsNodeType::File { + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDONLY) + .map_err(|e| >::try_into(e).unwrap())?; + let fsize = file.file_size(); + let _ = file.file_close(); + fsize + } else { + 0 // DIR size ? + }; + let blocks = (size + (BLOCK_SIZE as u64 - 1)) / BLOCK_SIZE as u64; + + info!( + "get_attr of {:?} {:?}, size: {}, blocks: {}", + vtype, + file.get_path(), + size, + blocks + ); + + Ok(VfsNodeAttr::new(perm, vtype, size, blocks)) + } + + fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { + info!("create {:?} on Ext4fs: {}", ty, path); + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + if fpath.is_empty() { + return Ok(()); + } + + let types = match ty { + VfsNodeType::Fifo => InodeTypes::EXT4_DE_FIFO, + VfsNodeType::CharDevice => InodeTypes::EXT4_DE_CHRDEV, + VfsNodeType::Dir => InodeTypes::EXT4_DE_DIR, + VfsNodeType::BlockDevice => InodeTypes::EXT4_DE_BLKDEV, + VfsNodeType::File => InodeTypes::EXT4_DE_REG_FILE, + VfsNodeType::SymLink => InodeTypes::EXT4_DE_SYMLINK, + VfsNodeType::Socket => InodeTypes::EXT4_DE_SOCK, + }; + + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, types.clone()) { + Ok(()) + } else { + if types == InodeTypes::EXT4_DE_DIR { + file.dir_mk(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } else { + file.file_open(fpath, O_WRONLY | O_CREAT | O_TRUNC) + .expect("create file failed"); + file.file_close() + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + } + } + + fn remove(&self, path: &RelPath) -> VfsResult { + info!("remove ext4fs: {}", path); + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + + assert!(!fpath.is_empty()); // already check at `root.rs` + + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_DIR) { + // Recursive directory remove + file.dir_rm(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } else { + file.file_remove(fpath) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + } + + /// Get the parent directory of this directory. + /// Return `None` if the node is a file. + fn parent(&self) -> Option { + let file = self.0.lock(); + if file.get_type() == InodeTypes::EXT4_DE_DIR { + let path = file.get_path(); + let path = path.to_str().unwrap(); + info!("Get the parent dir of {}", path); + let path = path.trim_end_matches('/').trim_end_matches(|c| c != '/'); + if !path.is_empty() { + return Some(Arc::new(Self::new(path, InodeTypes::EXT4_DE_DIR))); + } + } + None + } + + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult { + let file = self.0.lock(); + let (name, inode_type) = file.lwext4_dir_entries().unwrap(); + + let mut name_iter = name.iter().skip(start_idx); + let mut inode_type_iter = inode_type.iter().skip(start_idx); + + for (i, out_entry) in dirents.iter_mut().enumerate() { + let iname = name_iter.next(); + let itypes = inode_type_iter.next(); + + match itypes { + Some(t) => { + let ty = if *t == InodeTypes::EXT4_DE_DIR { + VfsNodeType::Dir + } else if *t == InodeTypes::EXT4_DE_REG_FILE { + VfsNodeType::File + } else if *t == InodeTypes::EXT4_DE_SYMLINK { + VfsNodeType::SymLink + } else { + error!("unknown file type: {:?}", itypes); + unreachable!() + }; + + *out_entry = + VfsDirEntry::new(core::str::from_utf8(iname.unwrap()).unwrap(), ty); + } + _ => return Ok(i), + } + } + + Ok(dirents.len()) + } + + /// Lookup the node with given `path` in the directory. + /// Return the node if found. + fn lookup(self: Arc, path: &RelPath) -> VfsResult { + debug!("lookup ext4fs: {:?}, {}", self.0.lock().get_path(), path); + + let fpath = self.path_deal_with(path); + let fpath = fpath.as_str(); + if fpath.is_empty() { + return Ok(self.clone()); + } + + ///////// + let mut file = self.0.lock(); + if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_DIR) { + debug!("lookup new DIR FileWrapper"); + Ok(Arc::new(Self::new(fpath, InodeTypes::EXT4_DE_DIR))) + } else if file.check_inode_exist(fpath, InodeTypes::EXT4_DE_REG_FILE) { + debug!("lookup new FILE FileWrapper"); + Ok(Arc::new(Self::new(fpath, InodeTypes::EXT4_DE_REG_FILE))) + } else { + Err(VfsError::NotFound) + } + } + + fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { + info!("To read_at {}, buf len={}", offset, buf.len()); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDONLY) + .map_err(|e| >::try_into(e).unwrap())?; + + file.file_seek(offset as i64, SEEK_SET) + .map_err(|e| >::try_into(e).unwrap())?; + let r = file.file_read(buf); + + let _ = file.file_close(); + r.map_err(|e| e.try_into().unwrap()) + } + + fn write_at(&self, offset: u64, buf: &[u8]) -> VfsResult { + info!("To write_at {}, buf len={}", offset, buf.len()); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDWR) + .map_err(|e| >::try_into(e).unwrap())?; + + file.file_seek(offset as i64, SEEK_SET) + .map_err(|e| >::try_into(e).unwrap())?; + let r = file.file_write(buf); + + let _ = file.file_close(); + r.map_err(|e| e.try_into().unwrap()) + } + + fn truncate(&self, size: u64) -> VfsResult { + info!("truncate file to size={}", size); + let mut file = self.0.lock(); + let path = file.get_path(); + let path = path.to_str().unwrap(); + file.file_open(path, O_RDWR | O_CREAT | O_TRUNC) + .map_err(|e| >::try_into(e).unwrap())?; + + let t = file.file_truncate(size); + + let _ = file.file_close(); + t.map(|_v| ()).map_err(|e| e.try_into().unwrap()) + } + + fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { + info!("rename from {} to {}", src_path, dst_path); + let mut file = self.0.lock(); + file.file_rename(src_path, dst_path) + .map(|_v| ()) + .map_err(|e| e.try_into().unwrap()) + } + + fn as_any(&self) -> &dyn core::any::Any { + self as &dyn core::any::Any + } +} + +impl Drop for FileWrapper { + fn drop(&mut self) { + let mut file = self.0.lock(); + debug!("Drop struct FileWrapper {:?}", file.get_path()); + file.file_close().expect("failed to close fd"); + drop(file); // todo + } +} + +impl KernelDevOp for Disk { + //type DevType = Box; + type DevType = Disk; + + fn read(dev: &mut Disk, mut buf: &mut [u8]) -> Result { + debug!("READ block device buf={}", buf.len()); + let mut read_len = 0; + while !buf.is_empty() { + match dev.read_one(buf) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + read_len += n; + } + Err(_e) => return Err(-1), + } + } + debug!("READ rt len={}", read_len); + Ok(read_len) + } + fn write(dev: &mut Self::DevType, mut buf: &[u8]) -> Result { + debug!("WRITE block device buf={}", buf.len()); + let mut write_len = 0; + while !buf.is_empty() { + match dev.write_one(buf) { + Ok(0) => break, + Ok(n) => { + buf = &buf[n..]; + write_len += n; + } + Err(_e) => return Err(-1), + } + } + debug!("WRITE rt len={}", write_len); + Ok(write_len) + } + fn flush(_dev: &mut Self::DevType) -> Result { + Ok(0) + } + fn seek(dev: &mut Disk, off: i64, whence: i32) -> Result { + let size = dev.size(); + debug!( + "SEEK block device size:{}, pos:{}, offset={}, whence={}", + size, + &dev.position(), + off, + whence + ); + let new_pos = match whence as u32 { + SEEK_SET => Some(off), + SEEK_CUR => dev.position().checked_add_signed(off).map(|v| v as i64), + SEEK_END => size.checked_add_signed(off).map(|v| v as i64), + _ => { + error!("invalid seek() whence: {}", whence); + Some(off) + } + } + .ok_or(-1)?; + + if new_pos as u64 > size { + warn!("Seek beyond the end of the block device"); + } + dev.set_position(new_pos as u64); + Ok(new_pos) + } +} \ No newline at end of file diff --git a/modules/ruxfs/src/fs/mod.rs b/modules/ruxfs/src/fs/mod.rs index 1e976b9a2..0c7cdec10 100644 --- a/modules/ruxfs/src/fs/mod.rs +++ b/modules/ruxfs/src/fs/mod.rs @@ -12,6 +12,12 @@ cfg_if::cfg_if! { pub mod myfs; } else if #[cfg(feature = "fatfs")] { pub mod fatfs; + } else if #[cfg(feature = "lwext4_rust")] { + pub mod lwext4_rust; + } else if #[cfg(feature = "ext4_rs")] { + pub mod ext4_rs; + } else if #[cfg(feature = "another_ext4")] { + pub mod another_ext4; } } diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index 2a72442e5..ab104f537 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -58,6 +58,15 @@ cfg_if::cfg_if! { } else if #[cfg(feature = "fatfs")] { use lazy_init::LazyInit; use alloc::sync::Arc; + } else if #[cfg(feature = "lwext4_rust")] { + use lazy_init::LazyInit; + use alloc::sync::Arc; + } else if #[cfg(feature = "ext4_rs")] { + use lazy_init::LazyInit; + use alloc::sync::Arc; + } else if #[cfg(feature = "another_ext4")] { + use lazy_init::LazyInit; + use alloc::sync::Arc; } } @@ -85,6 +94,18 @@ pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint FAT_FS.init_by(Arc::new(fs::fatfs::FatFileSystem::new(disk))); FAT_FS.init(); let blk_fs = FAT_FS.clone(); + } else if #[cfg(feature = "lwext4_rust")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::lwext4_rust::Ext4FileSystem::new(disk))); + let blk_fs = EXT4_FS.clone(); + } else if #[cfg(feature = "ext4_rs")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::ext4_rs::Ext4FileSystem::new(disk))); + let blk_fs = EXT4_FS.clone(); + } else if #[cfg(feature = "another_ext4")] { + static EXT4_FS: LazyInit> = LazyInit::new(); + EXT4_FS.init_by(Arc::new(fs::another_ext4::Ext4FileSystem::new(disk))); + let blk_fs = EXT4_FS.clone(); } } diff --git a/ulib/axstd/Cargo.toml b/ulib/axstd/Cargo.toml index 8f57a458c..074266863 100644 --- a/ulib/axstd/Cargo.toml +++ b/ulib/axstd/Cargo.toml @@ -51,6 +51,10 @@ myfs = ["arceos_api/myfs", "ruxfeat/myfs"] blkfs = ["ruxfeat/blkfs"] virtio-9p = ["ruxfeat/virtio-9p"] net-9p = ["ruxfeat/net-9p"] +fatfs = ["blkfs", "ruxfeat/fatfs"] +lwext4_rust = ["blkfs", "ruxfeat/lwext4_rust"] +ext4_rs = ["blkfs", "ruxfeat/ext4_rs"] +another_ext4 = ["blkfs", "ruxfeat/another_ext4"] # Networking net = ["arceos_api/net", "ruxfeat/net"] From fe343c1a8ecad6f7b0ccacbd853007475f710313 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 14 Nov 2024 14:53:37 +0800 Subject: [PATCH 16/36] feat: make ext4 fs scripts --- Makefile | 9 ++++++++- scripts/make/utils.mk | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 97b46532b..7c76c7c44 100644 --- a/Makefile +++ b/Makefile @@ -261,13 +261,20 @@ unittest: unittest_no_fail_fast: $(call unit_test,--no-fail-fast) -disk_img: +fat_img: ifneq ($(wildcard $(DISK_IMG)),) @printf "$(YELLOW_C)warning$(END_C): disk image \"$(DISK_IMG)\" already exists!\n" else $(call make_disk_image,fat32,$(DISK_IMG)) endif +ext4_img: +ifneq ($(wildcard $(DISK_IMG)),) + @printf "$(YELLOW_C)warning$(END_C): disk image \"$(DISK_IMG)\" already exists!\n" +else + $(call make_disk_image,ext4,$(DISK_IMG)) +endif + clean: clean_c clean_musl rm -rf $(APP)/*.bin $(APP)/*.elf cargo clean diff --git a/scripts/make/utils.mk b/scripts/make/utils.mk index 21bac486f..332f8d41d 100644 --- a/scripts/make/utils.mk +++ b/scripts/make/utils.mk @@ -18,6 +18,13 @@ define make_disk_image_fat32 @mkfs.fat -F 32 $(1) endef +define make_disk_image_ext4 + @printf " $(GREEN_C)Creating$(END_C) EXT4 disk image \"$(1)\" ...\n" + @dd if=/dev/zero of=$(1) bs=1M count=1024 + @mkfs.ext4 -b 4096 $(1) +endef + define make_disk_image $(if $(filter $(1),fat32), $(call make_disk_image_fat32,$(2))) + $(if $(filter $(1),ext4), $(call make_disk_image_ext4,$(2))) endef From f81ddc7767ac0939514d75970d59ea700265a2dc Mon Sep 17 00:00:00 2001 From: liujingx Date: Mon, 18 Nov 2024 16:55:16 +0800 Subject: [PATCH 17/36] fix: path canonicalization --- crates/axfs_vfs/src/path.rs | 150 ++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 65 deletions(-) diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index d798875d0..ace15a0c6 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -13,56 +13,11 @@ use alloc::{ borrow::{Cow, ToOwned}, format, string::String }; -/// Returns the canonical form of the path with all intermediate components -/// normalized. -/// -/// It won't force convert the path to an absolute form. -/// -/// # Examples -/// -/// ``` -/// use axfs_vfs::path::canonicalize; -/// -/// assert_eq!(canonicalize("/path/./to//foo"), "/path/to/foo"); -/// assert_eq!(canonicalize("/./path/to/../bar.rs"), "/path/bar.rs"); -/// assert_eq!(canonicalize("./foo/./bar"), "foo/bar"); -/// ``` -pub fn canonicalize(path: &str) -> String { - let mut buf = String::new(); - let is_absolute = path.starts_with('/'); - for part in path.split('/') { - match part { - "" | "." => continue, - ".." => { - while !buf.is_empty() { - if buf == "/" { - break; - } - let c = buf.pop().unwrap(); - if c == '/' { - break; - } - } - } - _ => { - if buf.is_empty() { - if is_absolute { - buf.push('/'); - } - } else if &buf[buf.len() - 1..] != "/" { - buf.push('/'); - } - buf.push_str(part); - } - } - } - if is_absolute && buf.is_empty() { - buf.push('/'); - } - buf -} - -/// CANONICALIZED absolute path type, starting with '/'. +/// Canonicalized absolute path type. +/// +/// - Starting with `/` +/// - No `.` or `..` components +/// - No redundant or tailing `/` /// /// Using `Cow` type to avoid unnecessary allocations. #[derive(Debug, Clone, PartialEq, Eq)] @@ -70,16 +25,23 @@ pub struct AbsPath<'a>(Cow<'a, str>); impl<'a> AbsPath<'a> { /// Simply wrap a str slice into a `AbsPath`. + /// + /// Caller should ensure that the path is absolute and canonicalized. pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } /// Simply wrap a string into a `AbsPath`. + /// + /// Caller should ensure that the path is absolute and canonicalized. pub const fn new_owned(path: String) -> Self { Self(Cow::Owned(path)) } /// Parse and canonicalize an absolute path from a string. + /// + /// - If the given path is not canonicalized, it will be canonicalized. + /// - If the given path is not absolute, it will be prefixed with `/`. pub fn new_canonicalized(path: &str) -> Self { if !path.starts_with('/') { Self(Cow::Owned(canonicalize(&("/".to_owned() + path)))) @@ -88,12 +50,12 @@ impl<'a> AbsPath<'a> { } } - /// Transform into a `RelPath`. + /// Trim the starting `/` to transform this `AbsPath` into a `RelPath`. pub fn to_rel(&self) -> RelPath { RelPath(Cow::Borrowed(self.0.trim_start_matches('/'))) } - /// Concatenate a relative path to this absolute path. + /// Concatenate a `RelPath` to this `AbsPath`. pub fn join(&self, rel: &RelPath) -> Self { Self::new_canonicalized(&format!("{}/{}", self.0, rel.0)) } @@ -113,15 +75,12 @@ impl core::fmt::Display for AbsPath<'_> { } } -/// CANONICALIZED relative path type, no starting '.' or '/'. -/// possibly starts with '..'. -/// -/// Valid examples: -/// - "" -/// - ".." -/// - "../b" -/// - "../.." -/// - "a/b/c" +/// Canonicalized relative path type. +/// +/// - No starting '.' or '/' +/// - No redundant or tailing '/' +/// - Possibly starts with '..' +/// - Valid examples: "", "..", "../b", "../.." /// /// Using `Cow` type to avoid unnecessary allocations. #[derive(Debug, Clone, PartialEq, Eq)] @@ -129,16 +88,23 @@ pub struct RelPath<'a>(Cow<'a, str>); impl<'a> RelPath<'a> { /// Simply wrap a string into a `RelPath`. + /// + /// Caller should ensure that the path is relative and canonicalized. pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } /// Wrap a string into a `RelPath` with possibly leading '/' trimmed. + /// + /// Caller should ensure that the path is canonicalized. pub fn new_trimmed(path: &'a str) -> Self { Self(Cow::Borrowed(path.trim_start_matches('/'))) } /// Parse and canonicalize a relative path from a string. + /// + /// - If the given path is not canonicalized, it will be canonicalized. + /// - If the given path is absolute, the starting '/' will be trimmed. pub fn new_canonicalized(path: &str) -> Self { Self(Cow::Owned(canonicalize(path.trim_start_matches('/')))) } @@ -158,6 +124,60 @@ impl core::fmt::Display for RelPath<'_> { } } +/// Returns the canonical form of the path with all intermediate components +/// normalized. +/// +/// It won't force convert the path to an absolute form. +/// +/// # Examples +/// +/// ``` +/// use axfs_vfs::path::canonicalize; +/// +/// assert_eq!(canonicalize("/path/./to//foo"), "/path/to/foo"); +/// assert_eq!(canonicalize("/./path/to/../bar.rs"), "/path/bar.rs"); +/// assert_eq!(canonicalize("./foo/./bar"), "foo/bar"); +/// assert_eq!(canonicalize("../foo/.."), ".."); +/// ``` +pub fn canonicalize(path: &str) -> String { + let mut buf = String::new(); + let is_absolute = path.starts_with('/'); + for part in path.split('/') { + match part { + "" | "." => continue, + ".." => { + if !is_absolute && buf.is_empty(){ + buf.push_str(".."); + continue; + } + while !buf.is_empty() { + if buf == "/" { + break; + } + let c = buf.pop().unwrap(); + if c == '/' { + break; + } + } + } + _ => { + if buf.is_empty() { + if is_absolute { + buf.push('/'); + } + } else if &buf[buf.len() - 1..] != "/" { + buf.push('/'); + } + buf.push_str(part); + } + } + } + if is_absolute && buf.is_empty() { + buf.push('/'); + } + buf +} + #[cfg(test)] mod tests { use super::*; @@ -170,9 +190,9 @@ mod tests { assert_eq!(canonicalize("/a/../"), "/"); assert_eq!(canonicalize("/a/../..///"), "/"); assert_eq!(canonicalize("a/../"), ""); - assert_eq!(canonicalize("a/..//.."), ""); + assert_eq!(canonicalize("a/..//.."), ".."); assert_eq!(canonicalize("././a"), "a"); - assert_eq!(canonicalize(".././a"), "a"); + assert_eq!(canonicalize(".././a"), "../a"); assert_eq!(canonicalize("/././a"), "/a"); assert_eq!(canonicalize("/abc/../abc"), "/abc"); assert_eq!(canonicalize("/test"), "/test"); @@ -191,8 +211,8 @@ mod tests { assert_eq!(canonicalize("/test//./../foo/bar//"), "/foo/bar"); assert_eq!(canonicalize("/test/../foo"), "/foo"); assert_eq!(canonicalize("/test/bar/../foo"), "/test/foo"); - assert_eq!(canonicalize("../foo"), "foo"); - assert_eq!(canonicalize("../foo/"), "foo"); + assert_eq!(canonicalize("../foo"), "../foo"); + assert_eq!(canonicalize("../foo/"), "../foo"); assert_eq!(canonicalize("/../foo"), "/foo"); assert_eq!(canonicalize("/../foo/"), "/foo"); assert_eq!(canonicalize("/../../foo"), "/foo"); From 929d3cec59520af574ae484e64b227e053b1a5f8 Mon Sep 17 00:00:00 2001 From: liujingx Date: Mon, 18 Nov 2024 21:41:45 +0800 Subject: [PATCH 18/36] feat: vfs add link and setattr fn --- crates/axfs_vfs/src/lib.rs | 35 ++++++++++++++++++++++++++--------- crates/axfs_vfs/src/macros.rs | 22 +++++++++++++++++++--- crates/axfs_vfs/src/path.rs | 6 ++++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/crates/axfs_vfs/src/lib.rs b/crates/axfs_vfs/src/lib.rs index 15f5db2ab..44a0bd14d 100644 --- a/crates/axfs_vfs/src/lib.rs +++ b/crates/axfs_vfs/src/lib.rs @@ -48,14 +48,13 @@ extern crate alloc; mod macros; +mod path; mod structs; -pub mod path; - use alloc::sync::Arc; use axerrno::{ax_err, AxError, AxResult}; -use path::{AbsPath, RelPath}; +pub use self::path::{AbsPath, RelPath}; pub use self::structs::{FileSystemInfo, VfsDirEntry, VfsNodeAttr, VfsNodePerm, VfsNodeType}; /// A wrapper of [`Arc`]. @@ -110,6 +109,19 @@ pub trait VfsNodeOps: Send + Sync { ax_err!(Unsupported) } + /// Set the attributes of the node. + /// + /// TODO: add time attributes + fn setattr( + &mut self, + _mode: Option, + _uid: Option, + _gid: Option, + _size: Option, + ) -> VfsResult { + ax_err!(Unsupported) + } + // file operations: /// Read data from the file at the given offset. @@ -155,18 +167,23 @@ pub trait VfsNodeOps: Send + Sync { ax_err!(Unsupported) } - /// Remove the node with the given `path` in the directory. - fn remove(&self, _path: &RelPath) -> VfsResult { + /// Create a new hard link to the src dentry + fn link(&self, _name: &RelPath, _src: Arc) -> VfsResult> { ax_err!(Unsupported) } - /// Read directory entries into `dirents`, starting from `start_idx`. - fn read_dir(&self, _start_idx: usize, _dirents: &mut [VfsDirEntry]) -> VfsResult { + /// Remove (the hard link of) the node with the given `path` in the directory. + fn unlink(&self, _path: &RelPath) -> VfsResult { + ax_err!(Unsupported) + } + + /// Rename the node `src_path` to `dst_path` in the directory. + fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult<()> { ax_err!(Unsupported) } - /// Renames or moves existing file or directory. - fn rename(&self, _src_path: &RelPath, _dst_path: &RelPath) -> VfsResult { + /// Read directory entries into `dirents`, starting from `start_idx`. + fn read_dir(&self, _start_idx: usize, _dirents: &mut [VfsDirEntry]) -> VfsResult { ax_err!(Unsupported) } diff --git a/crates/axfs_vfs/src/macros.rs b/crates/axfs_vfs/src/macros.rs index 859cf0408..b0eb3c39a 100644 --- a/crates/axfs_vfs/src/macros.rs +++ b/crates/axfs_vfs/src/macros.rs @@ -46,16 +46,32 @@ macro_rules! impl_vfs_non_dir_default { () => { fn lookup( self: $crate::__priv::Arc, - _path: &$crate::path::RelPath, + _path: &$crate::RelPath, ) -> $crate::VfsResult<$crate::VfsNodeRef> { $crate::__priv::ax_err!(NotADirectory) } - fn create(&self, _path: &$crate::path::RelPath, _ty: $crate::VfsNodeType) -> $crate::VfsResult { + fn create(&self, _path: &$crate::RelPath, _ty: $crate::VfsNodeType) -> $crate::VfsResult { $crate::__priv::ax_err!(NotADirectory) } - fn remove(&self, _path: &$crate::path::RelPath) -> $crate::VfsResult { + fn link( + &self, + _name: &$crate::RelPath, + _src: $crate::__priv::Arc, + ) -> VfsResult<$crate::__priv::Arc> { + $crate::__priv::ax_err!(NotADirectory) + } + + fn unlink(&self, _path: &$crate::RelPath) -> $crate::VfsResult { + $crate::__priv::ax_err!(NotADirectory) + } + + fn rename( + &self, + _src_path: &$crate::RelPath, + _dst_path: &$crate::RelPath, + ) -> $crate::VfsResult { $crate::__priv::ax_err!(NotADirectory) } diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index ace15a0c6..a430ff769 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -18,6 +18,7 @@ use alloc::{ /// - Starting with `/` /// - No `.` or `..` components /// - No redundant or tailing `/` +/// - Valid examples: "/", "/root/foo/bar" /// /// Using `Cow` type to avoid unnecessary allocations. #[derive(Debug, Clone, PartialEq, Eq)] @@ -77,7 +78,8 @@ impl core::fmt::Display for AbsPath<'_> { /// Canonicalized relative path type. /// -/// - No starting '.' or '/' +/// - No starting '/' +/// - No `.` components /// - No redundant or tailing '/' /// - Possibly starts with '..' /// - Valid examples: "", "..", "../b", "../.." @@ -139,7 +141,7 @@ impl core::fmt::Display for RelPath<'_> { /// assert_eq!(canonicalize("./foo/./bar"), "foo/bar"); /// assert_eq!(canonicalize("../foo/.."), ".."); /// ``` -pub fn canonicalize(path: &str) -> String { +fn canonicalize(path: &str) -> String { let mut buf = String::new(); let is_absolute = path.starts_with('/'); for part in path.split('/') { From 1ef7442a1e177940fa3189f05bd01f44b6b9cfcc Mon Sep 17 00:00:00 2001 From: liujingx Date: Mon, 18 Nov 2024 21:42:08 +0800 Subject: [PATCH 19/36] fix: ramfs and devfs support new vfs trait --- crates/axfs_devfs/src/dir.rs | 14 ++++++-------- crates/axfs_devfs/src/lib.rs | 2 +- crates/axfs_devfs/src/tests.rs | 2 +- crates/axfs_ramfs/src/dir.rs | 9 ++++----- crates/axfs_ramfs/src/lib.rs | 2 +- crates/axfs_ramfs/src/tests.rs | 22 +++++++++++----------- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/crates/axfs_devfs/src/dir.rs b/crates/axfs_devfs/src/dir.rs index 6ea67bfb8..6eb6985ca 100644 --- a/crates/axfs_devfs/src/dir.rs +++ b/crates/axfs_devfs/src/dir.rs @@ -9,8 +9,7 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; -use axfs_vfs::path::RelPath; -use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{RelPath, VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; @@ -116,22 +115,21 @@ impl VfsNodeOps for DirNode { } } - fn remove(&self, path: &RelPath) -> VfsResult { - log::debug!("remove at devfs: {}", path); + fn unlink(&self, path: &RelPath) -> VfsResult { + log::debug!("unlink at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.remove(&rest), - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(&rest), + ".." => self.parent().ok_or(VfsError::NotFound)?.unlink(&rest), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .remove(&rest), + .unlink(&rest), } } else { - Err(VfsError::PermissionDenied) // do not support to remove nodes dynamically + Err(VfsError::PermissionDenied) // do not support to unlink nodes dynamically } } diff --git a/crates/axfs_devfs/src/lib.rs b/crates/axfs_devfs/src/lib.rs index fef7e118c..4aa137f7f 100644 --- a/crates/axfs_devfs/src/lib.rs +++ b/crates/axfs_devfs/src/lib.rs @@ -29,7 +29,7 @@ pub use self::random::RandomDev; pub use self::zero::ZeroDev; use alloc::sync::Arc; -use axfs_vfs::{path::AbsPath, VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{AbsPath, VfsNodeRef, VfsOps, VfsResult}; use spin::once::Once; /// A device filesystem that implements [`axfs_vfs::VfsOps`]. diff --git a/crates/axfs_devfs/src/tests.rs b/crates/axfs_devfs/src/tests.rs index 815dab6a2..d33b60f25 100644 --- a/crates/axfs_devfs/src/tests.rs +++ b/crates/axfs_devfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{path::RelPath, VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{RelPath, VfsError, VfsNodeType, VfsResult}; use crate::*; diff --git a/crates/axfs_ramfs/src/dir.rs b/crates/axfs_ramfs/src/dir.rs index 3740d2110..dc848b848 100644 --- a/crates/axfs_ramfs/src/dir.rs +++ b/crates/axfs_ramfs/src/dir.rs @@ -11,8 +11,7 @@ use alloc::collections::BTreeMap; use alloc::sync::{Arc, Weak}; use alloc::{string::String, vec::Vec}; -use axfs_vfs::path::RelPath; -use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{RelPath, VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; @@ -145,18 +144,18 @@ impl VfsNodeOps for DirNode { } } - fn remove(&self, path: &RelPath) -> VfsResult { + fn unlink(&self, path: &RelPath) -> VfsResult { log::debug!("remove at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - ".." => self.parent().ok_or(VfsError::NotFound)?.remove(&rest), + ".." => self.parent().ok_or(VfsError::NotFound)?.unlink(&rest), _ => self .children .read() .get(name) .ok_or(VfsError::NotFound)? - .remove(&rest), + .unlink(&rest), } } else if name.is_empty() || name == ".." { Err(VfsError::InvalidInput) // remove '.' or '.. diff --git a/crates/axfs_ramfs/src/lib.rs b/crates/axfs_ramfs/src/lib.rs index 9dc159b2a..1612dccd7 100644 --- a/crates/axfs_ramfs/src/lib.rs +++ b/crates/axfs_ramfs/src/lib.rs @@ -25,7 +25,7 @@ pub use self::dir::DirNode; pub use self::file::FileNode; use alloc::sync::Arc; -use axfs_vfs::{path::AbsPath, VfsNodeRef, VfsOps, VfsResult}; +use axfs_vfs::{AbsPath, VfsNodeRef, VfsOps, VfsResult}; use spin::once::Once; /// A RAM filesystem that implements [`axfs_vfs::VfsOps`]. diff --git a/crates/axfs_ramfs/src/tests.rs b/crates/axfs_ramfs/src/tests.rs index ac5153558..b8c014dac 100644 --- a/crates/axfs_ramfs/src/tests.rs +++ b/crates/axfs_ramfs/src/tests.rs @@ -9,7 +9,7 @@ use std::sync::Arc; -use axfs_vfs::{path::RelPath, VfsError, VfsNodeType, VfsResult}; +use axfs_vfs::{RelPath, VfsError, VfsNodeType, VfsResult}; use crate::*; @@ -124,18 +124,18 @@ fn test_ramfs() { test_get_parent(&ramfs).unwrap(); let root = ramfs.root_dir(); - assert_eq!(root.remove(&RelPath::new_canonicalized("f1")), Ok(())); - assert_eq!(root.remove(&RelPath::new_canonicalized("//f2")), Ok(())); - assert_eq!(root.remove(&RelPath::new_canonicalized("f3")).err(), Some(VfsError::NotFound)); - assert_eq!(root.remove(&RelPath::new_canonicalized("foo")).err(), Some(VfsError::DirectoryNotEmpty)); - assert_eq!(root.remove(&RelPath::new_canonicalized("foo/..")).err(), Some(VfsError::InvalidInput)); + assert_eq!(root.unlink(&RelPath::new_canonicalized("f1")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("//f2")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("f3")).err(), Some(VfsError::NotFound)); + assert_eq!(root.unlink(&RelPath::new_canonicalized("foo")).err(), Some(VfsError::DirectoryNotEmpty)); + assert_eq!(root.unlink(&RelPath::new_canonicalized("foo/..")).err(), Some(VfsError::InvalidInput)); assert_eq!( - root.remove(&RelPath::new_canonicalized("foo/./bar")).err(), + root.unlink(&RelPath::new_canonicalized("foo/./bar")).err(), Some(VfsError::DirectoryNotEmpty) ); - assert_eq!(root.remove(&RelPath::new_canonicalized("foo/bar/f4")), Ok(())); - assert_eq!(root.remove(&RelPath::new_canonicalized("foo/bar")), Ok(())); - assert_eq!(root.remove(&RelPath::new_canonicalized("./foo//.//f3")), Ok(())); - assert_eq!(root.remove(&RelPath::new_canonicalized("./foo")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("foo/bar/f4")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("foo/bar")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("./foo//.//f3")), Ok(())); + assert_eq!(root.unlink(&RelPath::new_canonicalized("./foo")), Ok(())); assert!(ramfs.root_dir_node().get_entries().is_empty()); } From d0a8d5801543572ce89bb1ba6fc2f9d2e7132b9e Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 21 Nov 2024 13:57:54 +0800 Subject: [PATCH 20/36] refactor: ruxfs and posix_api compats with new vfs --- api/ruxos_posix_api/src/imp/fs.rs | 4 +- modules/ruxfs/Cargo.toml | 2 +- modules/ruxfs/src/api/dir.rs | 2 +- modules/ruxfs/src/api/file.rs | 5 +- modules/ruxfs/src/api/mod.rs | 7 ++- modules/ruxfs/src/fops.rs | 17 +++---- modules/ruxfs/src/fs/another_ext4.rs | 4 +- modules/ruxfs/src/fs/ext4_rs.rs | 4 +- modules/ruxfs/src/fs/fatfs.rs | 4 +- modules/ruxfs/src/fs/lwext4_rust.rs | 4 +- modules/ruxfs/src/lib.rs | 4 +- modules/ruxfs/src/mounts.rs | 2 +- modules/ruxfs/src/root.rs | 70 ++++++++++++++++++---------- 13 files changed, 73 insertions(+), 56 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 5a60770ce..aef50e794 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -697,7 +697,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { Directory::from_fd(fd)? .inner .lock() - .remove(&path.to_rel())?; + .unlink(&path.to_rel())?; } } else { if attr.is_dir() { @@ -712,7 +712,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { Directory::from_fd(fd)? .inner .lock() - .remove(&path.to_rel())?; + .unlink(&path.to_rel())?; } } } diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 9f53dc2c2..5c4a24f55 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -26,7 +26,7 @@ use-ramdisk = [] alloc = ["axalloc"] fp_simd = [] -default = ["devfs", "ramfs", "fatfs", "procfs", "sysfs", "etcfs"] +default = ["devfs", "ramfs", "procfs", "sysfs", "etcfs"] [dependencies] log = "0.4" diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index 0ddf30b23..5e5d3e508 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -9,7 +9,7 @@ use alloc::string::String; use axerrno::ax_err; -use axfs_vfs::{path::AbsPath, VfsError}; +use axfs_vfs::{AbsPath, VfsError}; use axio::Result; use core::fmt; diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index 71b806d8f..dd64d298f 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -9,10 +9,7 @@ use crate::fops; use axerrno::ax_err; -use axfs_vfs::{ - path::{AbsPath, RelPath}, - VfsError, -}; +use axfs_vfs::{AbsPath, RelPath, VfsError}; use axio::{prelude::*, Result, SeekFrom}; use capability::Cap; use core::fmt; diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index 1dc720481..74c59ec78 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -17,8 +17,7 @@ pub use self::file::{File, FileType, Metadata, OpenOptions, Permissions}; use alloc::{string::String, vec::Vec}; use axerrno::ax_err; -use axfs_vfs::path::AbsPath; -use axfs_vfs::VfsError; +use axfs_vfs::{AbsPath, VfsError}; use axio::{self as io, prelude::*}; use crate::fops; @@ -96,8 +95,8 @@ pub fn remove_dir(path: &AbsPath) -> io::Result<()> { pub fn remove_file(path: &AbsPath) -> io::Result<()> { let node = fops::lookup(path)?; let attr = node.get_attr()?; - if !attr.is_dir() { - return ax_err!(NotADirectory); + if attr.is_dir() { + return ax_err!(IsADirectory); } if !attr.perm().owner_writable() { return ax_err!(PermissionDenied); diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 0f80fbf4d..6903f38ba 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -10,8 +10,7 @@ //! Low-level filesystem operations. use axerrno::{ax_err, ax_err_type, AxResult}; -use axfs_vfs::path::{AbsPath, RelPath}; -use axfs_vfs::{VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{AbsPath, RelPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axio::SeekFrom; use capability::{Cap, WithCap}; @@ -135,7 +134,9 @@ impl File { impl Directory { /// Access the underlying `VfsNode` fn access_node(&self) -> AxResult<&VfsNodeRef> { - self.node.access(Cap::EXECUTE).map_err(|_| VfsError::PermissionDenied) + self.node + .access(Cap::EXECUTE) + .map_err(|_| VfsError::PermissionDenied) } /// Creates an opened directory. @@ -203,11 +204,11 @@ impl Directory { /// /// This function will not check if the file (or directory) exits or removeable, /// check it with [`lookup`] first. - pub fn remove(&self, path: &RelPath) -> AxResult { - self.access_node()?.remove(path) + pub fn unlink(&self, path: &RelPath) -> AxResult { + self.access_node()?.unlink(path) } - /// Rename a file or directory to a new name. This only works then the new path + /// Rename a file or directory to a new name. This only works when the new path /// is in the same mounted fs. /// /// This function will not check if the old path or new path exists, check it with @@ -300,7 +301,7 @@ pub fn create_dir_all(path: &AbsPath) -> AxResult { /// This function will not check if the file exits or removeable, /// check it with [`lookup`] first. pub fn remove_file(path: &AbsPath) -> AxResult { - ROOT_DIR.remove(&path.to_rel()) + ROOT_DIR.unlink(&path.to_rel()) } /// Remove a directory given an absolute path. @@ -311,7 +312,7 @@ pub fn remove_dir(path: &AbsPath) -> AxResult { if ROOT_DIR.contains(path) { return ax_err!(PermissionDenied); } - ROOT_DIR.remove(&path.to_rel()) + ROOT_DIR.unlink(&path.to_rel()) } /// Rename a file given an old and a new absolute path. diff --git a/modules/ruxfs/src/fs/another_ext4.rs b/modules/ruxfs/src/fs/another_ext4.rs index 4a41ab7bf..0925b2771 100644 --- a/modules/ruxfs/src/fs/another_ext4.rs +++ b/modules/ruxfs/src/fs/another_ext4.rs @@ -4,7 +4,7 @@ use another_ext4::{ Block, BlockDevice, ErrCode as Ext4ErrorCode, Ext4, Ext4Error, FileType as EXt4FileType, InodeMode as Ext4InodeMode, BLOCK_SIZE as EXT4_BLOCK_SIZE, EXT4_ROOT_INO, }; -use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, RelPath}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; use axsync::Mutex; @@ -164,7 +164,7 @@ impl VfsNodeOps for Ext4VirtInode { .map_err(map_error) } - fn remove(&self, path: &RelPath) -> VfsResult { + fn unlink(&self, path: &RelPath) -> VfsResult { self.fs.unlink(self.id, path).map_err(map_error) } diff --git a/modules/ruxfs/src/fs/ext4_rs.rs b/modules/ruxfs/src/fs/ext4_rs.rs index 9ff6b1614..5b08440a0 100644 --- a/modules/ruxfs/src/fs/ext4_rs.rs +++ b/modules/ruxfs/src/fs/ext4_rs.rs @@ -2,7 +2,7 @@ use crate::dev::Disk; use alloc::sync::Arc; use alloc::vec; use alloc::vec::*; -use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, RelPath}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; use axsync::Mutex; use core::cell::RefCell; @@ -264,7 +264,7 @@ impl VfsNodeOps for Ext4FileWrapper { } /// Remove the node with the given `path` in the directory. - fn remove(&self, _path: &RelPath) -> VfsResult { + fn unlink(&self, _path: &RelPath) -> VfsResult { todo!() } diff --git a/modules/ruxfs/src/fs/fatfs.rs b/modules/ruxfs/src/fs/fatfs.rs index 047cbc459..15822b493 100644 --- a/modules/ruxfs/src/fs/fatfs.rs +++ b/modules/ruxfs/src/fs/fatfs.rs @@ -9,7 +9,7 @@ use alloc::sync::Arc; use axerrno::ax_err; -use axfs_vfs::path::RelPath; +use axfs_vfs::RelPath; use core::cell::UnsafeCell; use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult}; @@ -188,7 +188,7 @@ impl VfsNodeOps for DirWrapper<'static> { } } - fn remove(&self, path: &RelPath) -> VfsResult { + fn unlink(&self, path: &RelPath) -> VfsResult { debug!("remove at fatfs: {}", path); if path.is_empty() { return ax_err!(PermissionDenied) diff --git a/modules/ruxfs/src/fs/lwext4_rust.rs b/modules/ruxfs/src/fs/lwext4_rust.rs index db231d5e5..e6f375279 100644 --- a/modules/ruxfs/src/fs/lwext4_rust.rs +++ b/modules/ruxfs/src/fs/lwext4_rust.rs @@ -1,7 +1,7 @@ use crate::alloc::string::String; use alloc::sync::Arc; use axerrno::AxError; -use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, path::RelPath}; +use axfs_vfs::{VfsDirEntry, VfsError, VfsNodePerm, VfsResult, RelPath}; use axfs_vfs::{VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps}; use axsync::Mutex; use lwext4_rust::bindings::{ @@ -176,7 +176,7 @@ impl VfsNodeOps for FileWrapper { } } - fn remove(&self, path: &RelPath) -> VfsResult { + fn unlink(&self, path: &RelPath) -> VfsResult { info!("remove ext4fs: {}", path); let fpath = self.path_deal_with(path); let fpath = fpath.as_str(); diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index ab104f537..b86a51e3d 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -46,8 +46,8 @@ pub mod api; pub mod fops; pub mod root; -pub type AbsPath<'a> = axfs_vfs::path::AbsPath<'a>; -pub type RelPath<'a> = axfs_vfs::path::RelPath<'a>; +pub type AbsPath<'a> = axfs_vfs::AbsPath<'a>; +pub type RelPath<'a> = axfs_vfs::RelPath<'a>; use alloc::vec::Vec; diff --git a/modules/ruxfs/src/mounts.rs b/modules/ruxfs/src/mounts.rs index f3d08ebdc..a1380cbe3 100644 --- a/modules/ruxfs/src/mounts.rs +++ b/modules/ruxfs/src/mounts.rs @@ -8,7 +8,7 @@ */ use alloc::sync::Arc; -use axfs_vfs::{VfsNodeType, VfsOps, VfsResult, path::RelPath}; +use axfs_vfs::{VfsNodeType, VfsOps, VfsResult, RelPath}; #[cfg(feature = "alloc")] use crate::arch::{get_cpuinfo, get_meminfo}; diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index 83fa920db..e1c32fb35 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -14,8 +14,7 @@ use alloc::{format, sync::Arc, vec::Vec}; use axerrno::{ax_err, AxResult}; use axfs_vfs::{ - path::{AbsPath, RelPath}, - VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult, + AbsPath, RelPath, VfsError, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType, VfsOps, VfsResult, }; use axsync::Mutex; use lazy_init::LazyInit; @@ -31,6 +30,7 @@ pub struct MountPoint { fs: Arc, } +/// Root directory of the main filesystem pub(crate) struct RootDirectory { main_fs: Arc, mounts: Vec, @@ -69,11 +69,13 @@ impl RootDirectory { // create the mount point in the main filesystem if it does not exist match self.main_fs.root_dir().lookup(&path.to_rel()) { Ok(_) => {} - Err(err_code) => { - if err_code == VfsError::NotFound { + Err(e) => { + if e == VfsError::NotFound { self.main_fs .root_dir() .create(&path.to_rel(), FileType::Dir)?; + } else { + return Err(e); } } } @@ -90,10 +92,9 @@ impl RootDirectory { self.mounts.iter().any(|mp| mp.path == *path) } - fn lookup_mounted_fs(&self, path: &RelPath, f: F) -> AxResult - where - F: FnOnce(Arc, &RelPath) -> AxResult, - { + /// Check if path matches a mountpoint, return the index of the matched + /// mountpoint and the matched length. + fn lookup_mounted_fs(&self, path: &RelPath) -> (usize, usize) { debug!("lookup at root: {}", path); let mut idx = 0; let mut max_len = 0; @@ -101,17 +102,31 @@ impl RootDirectory { // Find the filesystem that has the longest mounted path match // TODO: more efficient, e.g. trie for (i, mp) in self.mounts.iter().enumerate() { - // skip the first '/' - if path.starts_with(&mp.path[1..]) && mp.path.len() - 1 > max_len { + let rel_mp = mp.path.to_rel(); + // path must have format: "" or "/..." + if (rel_mp == *path || path.starts_with(&format!("{}/", rel_mp))) + && rel_mp.len() > max_len + { max_len = mp.path.len() - 1; idx = i; } } + return (idx, max_len); + } - if max_len == 0 { - f(self.main_fs.clone(), path) // not matched any mount point + /// Check if path matches a mountpoint, dispatch the operation to the matched filesystem + fn lookup_mounted_fs_then(&self, path: &RelPath, f: F) -> AxResult + where + F: FnOnce(Arc, &RelPath) -> AxResult, + { + let (idx, len) = self.lookup_mounted_fs(path); + if len > 0 { + f( + self.mounts[idx].fs.clone(), + &RelPath::new_trimmed(&path[len..]), + ) } else { - f(self.mounts[idx].fs.clone(), &RelPath::new_trimmed(&path[max_len..])) // matched at `idx` + f(self.main_fs.clone(), path) } } } @@ -124,11 +139,11 @@ impl VfsNodeOps for RootDirectory { } fn lookup(self: Arc, path: &RelPath) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| fs.root_dir().lookup(rest_path)) + self.lookup_mounted_fs_then(path, |fs, rest_path| fs.root_dir().lookup(rest_path)) } fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| { + self.lookup_mounted_fs_then(path, |fs, rest_path| { if rest_path.is_empty() { Ok(()) // already exists } else { @@ -137,24 +152,29 @@ impl VfsNodeOps for RootDirectory { }) } - fn remove(&self, path: &RelPath) -> VfsResult { - self.lookup_mounted_fs(path, |fs, rest_path| { + fn unlink(&self, path: &RelPath) -> VfsResult { + self.lookup_mounted_fs_then(path, |fs, rest_path| { if rest_path.is_empty() { ax_err!(PermissionDenied) // cannot remove mount points } else { - fs.root_dir().remove(rest_path) + fs.root_dir().unlink(rest_path) } }) } fn rename(&self, src_path: &RelPath, dst_path: &RelPath) -> VfsResult { - self.lookup_mounted_fs(src_path, |fs, rest_path| { - if rest_path.is_empty() { - ax_err!(PermissionDenied) // cannot rename mount points - } else { - fs.root_dir().rename(rest_path, dst_path) - } - }) + let (src_idx, src_len) = self.lookup_mounted_fs(src_path); + let (dst_idx, dst_len) = self.lookup_mounted_fs(dst_path); + if src_idx != dst_idx { + return ax_err!(PermissionDenied); // cannot rename across mount points + } + if src_path.len() == src_len { + return ax_err!(PermissionDenied); // cannot rename mount points + } + self.mounts[src_idx].fs.root_dir().rename( + &RelPath::new_trimmed(&src_path[src_len..]), + &RelPath::new_trimmed(&dst_path[dst_len..]), + ) } } From ed49d8967038b1ef66c006be4d96daca76c5bad0 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 21 Nov 2024 15:45:39 +0800 Subject: [PATCH 21/36] feat: add inode number in vfsattr --- crates/axfs_devfs/src/dir.rs | 20 +++++++++++++++++--- crates/axfs_devfs/src/lib.rs | 26 +++++++++++++++++++++++++- crates/axfs_devfs/src/null.rs | 1 + crates/axfs_devfs/src/random.rs | 1 + crates/axfs_devfs/src/zero.rs | 1 + crates/axfs_ramfs/src/dir.rs | 21 +++++++++++++++++---- crates/axfs_ramfs/src/file.rs | 10 ++++++++-- crates/axfs_ramfs/src/lib.rs | 26 +++++++++++++++++++++++++- crates/axfs_vfs/src/structs.rs | 17 +++++++++++++---- 9 files changed, 108 insertions(+), 15 deletions(-) diff --git a/crates/axfs_devfs/src/dir.rs b/crates/axfs_devfs/src/dir.rs index 6eb6985ca..afc490d71 100644 --- a/crates/axfs_devfs/src/dir.rs +++ b/crates/axfs_devfs/src/dir.rs @@ -13,20 +13,30 @@ use axfs_vfs::{RelPath, VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNod use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; +use crate::InoAllocator; + /// The directory node in the device filesystem. /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct DirNode { + ino: u64, parent: RwLock>, children: RwLock>, + ialloc: Weak, } impl DirNode { - pub(super) fn new(parent: Option<&VfsNodeRef>) -> Arc { + pub(super) fn new( + ino: u64, + parent: Option<&VfsNodeRef>, + ialloc: Weak, + ) -> Arc { let parent = parent.map_or(Weak::::new() as _, Arc::downgrade); Arc::new(Self { + ino, parent: RwLock::new(parent), children: RwLock::new(BTreeMap::new()), + ialloc, }) } @@ -37,7 +47,11 @@ impl DirNode { /// Create a subdirectory at this directory. pub fn mkdir(self: &Arc, name: &'static str) -> Arc { let parent = self.clone() as VfsNodeRef; - let node = Self::new(Some(&parent)); + let node = Self::new( + self.ialloc.upgrade().unwrap().alloc(), + Some(&parent), + self.ialloc.clone(), + ); self.children.write().insert(name, node.clone()); node } @@ -50,7 +64,7 @@ impl DirNode { impl VfsNodeOps for DirNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_dir(4096, 0)) + Ok(VfsNodeAttr::new_dir(self.ino, 4096, 0)) } fn parent(&self) -> Option { diff --git a/crates/axfs_devfs/src/lib.rs b/crates/axfs_devfs/src/lib.rs index 4aa137f7f..fcb0a603f 100644 --- a/crates/axfs_devfs/src/lib.rs +++ b/crates/axfs_devfs/src/lib.rs @@ -30,20 +30,44 @@ pub use self::zero::ZeroDev; use alloc::sync::Arc; use axfs_vfs::{AbsPath, VfsNodeRef, VfsOps, VfsResult}; +use core::sync::atomic::AtomicU64; use spin::once::Once; +/// An auto-increasing inode number allocator. +pub struct InoAllocator { + current: AtomicU64, +} + +impl InoAllocator { + /// Create a new allocator instance. + pub fn new(start: u64) -> Self { + Self { + current: AtomicU64::new(start), + } + } + + /// Allocate a new inode number. + pub fn alloc(&self) -> u64 { + self.current + .fetch_add(1, core::sync::atomic::Ordering::SeqCst) + } +} + /// A device filesystem that implements [`axfs_vfs::VfsOps`]. pub struct DeviceFileSystem { parent: Once, root: Arc, + _ialloc: Arc, } impl DeviceFileSystem { /// Create a new instance. pub fn new() -> Self { + let ialloc = Arc::new(InoAllocator::new(10)); Self { parent: Once::new(), - root: DirNode::new(None), + root: DirNode::new(2, None, Arc::downgrade(&ialloc)), + _ialloc: ialloc, } } diff --git a/crates/axfs_devfs/src/null.rs b/crates/axfs_devfs/src/null.rs index 183428411..607e8092b 100644 --- a/crates/axfs_devfs/src/null.rs +++ b/crates/axfs_devfs/src/null.rs @@ -17,6 +17,7 @@ pub struct NullDev; impl VfsNodeOps for NullDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 3, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, diff --git a/crates/axfs_devfs/src/random.rs b/crates/axfs_devfs/src/random.rs index 2bf10fe77..c48a7fc05 100644 --- a/crates/axfs_devfs/src/random.rs +++ b/crates/axfs_devfs/src/random.rs @@ -30,6 +30,7 @@ fn rand_lcg32() -> u32 { impl VfsNodeOps for RandomDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 6, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, diff --git a/crates/axfs_devfs/src/zero.rs b/crates/axfs_devfs/src/zero.rs index b4244349a..10d04634e 100644 --- a/crates/axfs_devfs/src/zero.rs +++ b/crates/axfs_devfs/src/zero.rs @@ -17,6 +17,7 @@ pub struct ZeroDev; impl VfsNodeOps for ZeroDev { fn get_attr(&self) -> VfsResult { Ok(VfsNodeAttr::new( + 4, VfsNodePerm::default_file(), VfsNodeType::CharDevice, 0, diff --git a/crates/axfs_ramfs/src/dir.rs b/crates/axfs_ramfs/src/dir.rs index dc848b848..6dafd329f 100644 --- a/crates/axfs_ramfs/src/dir.rs +++ b/crates/axfs_ramfs/src/dir.rs @@ -16,22 +16,31 @@ use axfs_vfs::{VfsError, VfsResult}; use spin::RwLock; use crate::file::FileNode; +use crate::InoAllocator; /// The directory node in the RAM filesystem. /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct DirNode { + ino: u64, this: Weak, parent: RwLock>, children: RwLock>, + ialloc: Weak, } impl DirNode { - pub(super) fn new(parent: Option>) -> Arc { + pub(super) fn new( + ino: u64, + parent: Option>, + ialloc: Weak, + ) -> Arc { Arc::new_cyclic(|this| Self { + ino, this: this.clone(), parent: RwLock::new(parent.unwrap_or_else(|| Weak::::new())), children: RwLock::new(BTreeMap::new()), + ialloc, }) } @@ -56,8 +65,12 @@ impl DirNode { return Err(VfsError::AlreadyExists); } let node: VfsNodeRef = match ty { - VfsNodeType::File => Arc::new(FileNode::new()), - VfsNodeType::Dir => Self::new(Some(self.this.clone())), + VfsNodeType::File => Arc::new(FileNode::new(self.ialloc.upgrade().unwrap().alloc())), + VfsNodeType::Dir => Self::new( + self.ialloc.upgrade().unwrap().alloc(), + Some(self.this.clone()), + self.ialloc.clone(), + ), _ => return Err(VfsError::Unsupported), }; self.children.write().insert(name.into(), node); @@ -80,7 +93,7 @@ impl DirNode { impl VfsNodeOps for DirNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_dir(4096, 0)) + Ok(VfsNodeAttr::new_dir(self.ino, 4096, 0)) } fn parent(&self) -> Option { diff --git a/crates/axfs_ramfs/src/file.rs b/crates/axfs_ramfs/src/file.rs index 7464c5d4e..4ddfcd417 100644 --- a/crates/axfs_ramfs/src/file.rs +++ b/crates/axfs_ramfs/src/file.rs @@ -15,12 +15,14 @@ use spin::RwLock; /// /// It implements [`axfs_vfs::VfsNodeOps`]. pub struct FileNode { + ino: u64, content: RwLock>, } impl FileNode { - pub(super) const fn new() -> Self { + pub(super) const fn new(ino: u64) -> Self { Self { + ino, content: RwLock::new(Vec::new()), } } @@ -28,7 +30,11 @@ impl FileNode { impl VfsNodeOps for FileNode { fn get_attr(&self) -> VfsResult { - Ok(VfsNodeAttr::new_file(self.content.read().len() as _, 0)) + Ok(VfsNodeAttr::new_file( + self.ino, + self.content.read().len() as _, + 0, + )) } fn truncate(&self, size: u64) -> VfsResult { diff --git a/crates/axfs_ramfs/src/lib.rs b/crates/axfs_ramfs/src/lib.rs index 1612dccd7..a57751d42 100644 --- a/crates/axfs_ramfs/src/lib.rs +++ b/crates/axfs_ramfs/src/lib.rs @@ -26,20 +26,44 @@ pub use self::file::FileNode; use alloc::sync::Arc; use axfs_vfs::{AbsPath, VfsNodeRef, VfsOps, VfsResult}; +use core::sync::atomic::AtomicU64; use spin::once::Once; +/// An auto-increasing inode number allocator. +pub struct InoAllocator { + current: AtomicU64, +} + +impl InoAllocator { + /// Create a new allocator instance. + pub fn new(start: u64) -> Self { + Self { + current: AtomicU64::new(start), + } + } + + /// Allocate a new inode number. + pub fn alloc(&self) -> u64 { + self.current + .fetch_add(1, core::sync::atomic::Ordering::SeqCst) + } +} + /// A RAM filesystem that implements [`axfs_vfs::VfsOps`]. pub struct RamFileSystem { parent: Once, root: Arc, + _ialloc: Arc, } impl RamFileSystem { /// Create a new instance. pub fn new() -> Self { + let ialloc = Arc::new(InoAllocator::new(0)); Self { parent: Once::new(), - root: DirNode::new(None), + root: DirNode::new(ialloc.alloc(), None, Arc::downgrade(&ialloc)), + _ialloc: ialloc, } } diff --git a/crates/axfs_vfs/src/structs.rs b/crates/axfs_vfs/src/structs.rs index 7a4b3834b..c1a4e9873 100644 --- a/crates/axfs_vfs/src/structs.rs +++ b/crates/axfs_vfs/src/structs.rs @@ -17,6 +17,8 @@ pub struct FileSystemInfo; #[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct VfsNodeAttr { + /// Inode number. + ino: u64, /// File permission mode. mode: VfsNodePerm, /// File type. @@ -206,10 +208,11 @@ impl VfsNodeType { } impl VfsNodeAttr { - /// Creates a new `VfsNodeAttr` with the given permission mode, type, size + /// Creates a new `VfsNodeAttr` with the given inode number, permission mode, type, size /// and number of blocks. - pub const fn new(mode: VfsNodePerm, ty: VfsNodeType, size: u64, blocks: u64) -> Self { + pub const fn new(ino: u64, mode: VfsNodePerm, ty: VfsNodeType, size: u64, blocks: u64) -> Self { Self { + ino, mode, ty, size, @@ -218,8 +221,9 @@ impl VfsNodeAttr { } /// Creates a new `VfsNodeAttr` for a file, with the default file permission. - pub const fn new_file(size: u64, blocks: u64) -> Self { + pub const fn new_file(ino: u64, size: u64, blocks: u64) -> Self { Self { + ino, mode: VfsNodePerm::default_file(), ty: VfsNodeType::File, size, @@ -229,8 +233,9 @@ impl VfsNodeAttr { /// Creates a new `VfsNodeAttr` for a directory, with the default directory /// permission. - pub const fn new_dir(size: u64, blocks: u64) -> Self { + pub const fn new_dir(ino: u64, size: u64, blocks: u64) -> Self { Self { + ino, mode: VfsNodePerm::default_dir(), ty: VfsNodeType::Dir, size, @@ -238,6 +243,10 @@ impl VfsNodeAttr { } } + /// Returns the inode number of the node. + pub const fn ino(&self) -> u64 { + self.ino + } /// Returns the size of the node. pub const fn size(&self) -> u64 { self.size From 49decb2be0a4d860d3cfbcad137d5f403d563c30 Mon Sep 17 00:00:00 2001 From: liujingx Date: Mon, 25 Nov 2024 16:41:22 +0800 Subject: [PATCH 22/36] refactor: remove fops with relative path --- modules/ruxfs/src/api/dir.rs | 2 +- modules/ruxfs/src/api/file.rs | 6 +- modules/ruxfs/src/fops.rs | 155 +++++++++------------------------- modules/ruxfs/src/root.rs | 6 +- 4 files changed, 48 insertions(+), 121 deletions(-) diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index 5e5d3e508..c3852d6db 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -44,7 +44,7 @@ impl<'a> ReadDir<'a> { let mut opts = OpenOptions::new(); opts.read(true); let node = fops::lookup(path)?; - let inner = fops::open_dir(node, (&opts).into())?; + let inner = fops::open_dir(path, node, (&opts).into())?; const EMPTY: fops::DirEntry = fops::DirEntry::default(); let dirent_buf = [EMPTY; 31]; Ok(ReadDir { diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index dd64d298f..1a464b688 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -7,7 +7,7 @@ * See the Mulan PSL v2 for more details. */ -use crate::fops; +use crate::fops::{self, current_dir}; use axerrno::ax_err; use axfs_vfs::{AbsPath, RelPath, VfsError}; use axio::{prelude::*, Result, SeekFrom}; @@ -128,7 +128,7 @@ impl OpenOptions { let path = if path.starts_with("/") { AbsPath::new_canonicalized(path) } else { - fops::concat_path(&RelPath::new_canonicalized(path)) + current_dir().join(&RelPath::new_canonicalized(path)) }; // Check flag and attr let node = match fops::lookup(&path) { @@ -155,7 +155,7 @@ impl OpenOptions { node.truncate(0)?; } // Open - fops::open_file(node, self.into(), self.append).map(|inner| File { inner }) + fops::open_file(&path, node, self.into(), self.append).map(|inner| File { inner }) } } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 6903f38ba..5bda98ac2 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -9,12 +9,13 @@ //! Low-level filesystem operations. +use alloc::borrow::ToOwned; use axerrno::{ax_err, ax_err_type, AxResult}; -use axfs_vfs::{AbsPath, RelPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; +use axfs_vfs::{AbsPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axio::SeekFrom; use capability::{Cap, WithCap}; -use crate::root::{CURRENT_DIR, CURRENT_DIR_PATH, ROOT_DIR}; +use crate::root::{CURRENT_DIR, ROOT_DIR}; #[cfg(feature = "myfs")] pub use crate::dev::Disk; @@ -32,32 +33,36 @@ pub type FilePerm = axfs_vfs::VfsNodePerm; /// An opened file object, with open permissions and a cursor. pub struct File { + path: AbsPath<'static>, node: WithCap, is_append: bool, offset: u64, } -/// An opened directory object, with open permissions and a cursor for -/// [`read_dir`](Directory::read_dir). -pub struct Directory { - node: WithCap, - entry_idx: usize, -} - impl File { /// Create an opened file. - pub fn new(node: VfsNodeRef, cap: Cap, is_append: bool) -> Self { + pub fn new(path: AbsPath<'static>, node: VfsNodeRef, cap: Cap, is_append: bool) -> Self { Self { + path, node: WithCap::new(node, cap), offset: 0, is_append, } } + /// Get the abcolute path of the file. + pub fn path(&self) -> &AbsPath { + &self.path + } + + /// Gets the file attributes. + pub fn get_attr(&self) -> AxResult { + self.node.access(Cap::empty())?.get_attr() + } + /// Truncates the file to the specified size. pub fn truncate(&self, size: u64) -> AxResult { - self.node.access(Cap::WRITE)?.truncate(size)?; - Ok(()) + self.node.access(Cap::WRITE)?.truncate(size) } /// Reads the file at the current position. Returns the number of bytes @@ -65,8 +70,7 @@ impl File { /// /// After the read, the cursor will be advanced by the number of bytes read. pub fn read(&mut self, buf: &mut [u8]) -> AxResult { - let node = self.node.access(Cap::READ)?; - let read_len = node.read_at(self.offset, buf)?; + let read_len = self.node.access(Cap::READ)?.read_at(self.offset, buf)?; self.offset += read_len as u64; Ok(read_len) } @@ -75,9 +79,7 @@ impl File { /// /// It does not update the file cursor. pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> AxResult { - let node = self.node.access(Cap::READ)?; - let read_len = node.read_at(offset, buf)?; - Ok(read_len) + self.node.access(Cap::READ)?.read_at(offset, buf) } /// Writes the file at the current position. Returns the number of bytes @@ -100,15 +102,12 @@ impl File { /// /// It does not update the file cursor. pub fn write_at(&self, offset: u64, buf: &[u8]) -> AxResult { - let node = self.node.access(Cap::WRITE)?; - let write_len = node.write_at(offset, buf)?; - Ok(write_len) + self.node.access(Cap::WRITE)?.write_at(offset, buf) } /// Flushes the file, writes all buffered data to the underlying device. pub fn flush(&self) -> AxResult { - self.node.access(Cap::WRITE)?.fsync()?; - Ok(()) + self.node.access(Cap::WRITE)?.fsync() } /// Sets the cursor of the file to the specified offset. Returns the new @@ -124,99 +123,36 @@ impl File { self.offset = new_offset; Ok(new_offset) } +} - /// Gets the file attributes. - pub fn get_attr(&self) -> AxResult { - self.node.access(Cap::empty())?.get_attr() - } +/// An opened directory object, with open permissions and a cursor for +/// [`read_dir`](Directory::read_dir). +pub struct Directory { + path: AbsPath<'static>, + node: WithCap, + entry_idx: usize, } impl Directory { - /// Access the underlying `VfsNode` - fn access_node(&self) -> AxResult<&VfsNodeRef> { - self.node - .access(Cap::EXECUTE) - .map_err(|_| VfsError::PermissionDenied) - } - /// Creates an opened directory. - pub fn new(node: VfsNodeRef, cap: Cap) -> Self { + pub fn new(path: AbsPath<'static>, node: VfsNodeRef, cap: Cap) -> Self { Self { + path, node: WithCap::new(node, cap), entry_idx: 0, } } + /// Gets the absolute path of the directory. + pub fn path(&self) -> &AbsPath { + &self.path + } + /// Gets the file attributes. pub fn get_attr(&self) -> AxResult { self.node.access(Cap::empty())?.get_attr() } - /// Looks up a child node by name. - pub fn lookup(&self, path: &RelPath) -> AxResult { - self.access_node()?.clone().lookup(path) - } - - /// Gets the file attributes of the file at the path relative to this directory. - /// Returns a [`FileAttr`] object. - pub fn get_child_attr(&self, path: &RelPath) -> AxResult { - let node = self.lookup(path)?; - node.get_attr() - } - - /// Opens a node as a directory, with permission checked. - pub fn open_dir(&self, node: VfsNodeRef, cap: Cap) -> AxResult { - let attr = node.get_attr()?; - if !perm_to_cap(attr.perm()).contains(cap) { - return ax_err!(PermissionDenied); - } - node.open()?; - Ok(Self::new(node, cap | Cap::EXECUTE)) - } - - /// Opens a node as a file, with permission checked. - pub fn open_file(&self, node: VfsNodeRef, cap: Cap, append: bool) -> AxResult { - let attr = node.get_attr()?; - if !perm_to_cap(attr.perm()).contains(cap) { - return ax_err!(PermissionDenied); - } - node.open()?; - Ok(File::new(node, cap, append)) - } - - /// Creates an empty file at the path relative to this directory. - /// - /// This function will not check if the path exists, check it with - /// [`lookup`] first. - pub fn create_file(&self, path: &RelPath) -> AxResult { - self.access_node()?.clone().create(path, VfsNodeType::File) - } - - /// Creates an empty directory at the path relative to this directory. - /// - /// This function will not check if the path exists, check it with - /// [`lookup`] first. - pub fn create_dir(&self, path: &RelPath) -> AxResult { - self.access_node()?.clone().create(path, VfsNodeType::Dir) - } - - /// Removes a file (or directory) at the path relative to this directory. - /// - /// This function will not check if the file (or directory) exits or removeable, - /// check it with [`lookup`] first. - pub fn unlink(&self, path: &RelPath) -> AxResult { - self.access_node()?.unlink(path) - } - - /// Rename a file or directory to a new name. This only works when the new path - /// is in the same mounted fs. - /// - /// This function will not check if the old path or new path exists, check it with - /// [`lookup`] first. - pub fn rename(&self, old: &RelPath, new: &RelPath) -> AxResult { - self.access_node()?.rename(old, new) - } - /// Reads directory entries starts from the current position into the /// given buffer. Returns the number of entries read. /// @@ -251,28 +187,27 @@ pub fn lookup(path: &AbsPath) -> AxResult { /// Get the file attributes given an absolute path. pub fn get_attr(path: &AbsPath) -> AxResult { - let node = lookup(path)?; - node.get_attr() + lookup(path)?.get_attr() } /// Open a node as a file, with permission checked. -pub fn open_file(node: VfsNodeRef, cap: Cap, append: bool) -> AxResult { +pub fn open_file(path: &AbsPath, node: VfsNodeRef, cap: Cap, append: bool) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); } node.open()?; - Ok(File::new(node, cap, append)) + Ok(File::new(path.to_owned(), node, cap, append)) } /// Open a node as a directory, with permission checked. -pub fn open_dir(node: VfsNodeRef, cap: Cap) -> AxResult { +pub fn open_dir(path: &AbsPath, node: VfsNodeRef, cap: Cap) -> AxResult { let attr = node.get_attr()?; if !perm_to_cap(attr.perm()).contains(cap) { return ax_err!(PermissionDenied); } node.open()?; - Ok(Directory::new(node, cap | Cap::EXECUTE)) + Ok(Directory::new(path.to_owned(), node, cap | Cap::EXECUTE)) } /// Create a file given an absolute path. @@ -325,12 +260,7 @@ pub fn rename(old: &AbsPath, new: &AbsPath) -> AxResult { /// Get current working directory. pub fn current_dir<'a>() -> AbsPath<'a> { - CURRENT_DIR_PATH.lock().clone() -} - -/// Concatenate a path to the current working directory. -pub fn concat_path(path: &RelPath) -> AbsPath<'static> { - current_dir().join(path) + CURRENT_DIR.lock().clone() } /// Set current working directory. @@ -344,8 +274,7 @@ pub fn set_current_dir(path: AbsPath<'static>) -> AxResult { } else if !attr.perm().owner_executable() { Err(VfsError::PermissionDenied) } else { - *CURRENT_DIR.lock() = node; - *CURRENT_DIR_PATH.lock() = path; + *CURRENT_DIR.lock() = path; Ok(()) } } diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index e1c32fb35..fa0a713ae 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -21,8 +21,7 @@ use lazy_init::LazyInit; use crate::api::FileType; -pub(crate) static CURRENT_DIR_PATH: Mutex = Mutex::new(AbsPath::new("/")); -pub(crate) static CURRENT_DIR: LazyInit> = LazyInit::new(); +pub(crate) static CURRENT_DIR: Mutex = Mutex::new(AbsPath::new("/")); /// mount point information pub struct MountPoint { @@ -194,6 +193,5 @@ pub(crate) fn init_rootfs(mount_points: Vec) { } ROOT_DIR.init_by(Arc::new(root_dir)); - CURRENT_DIR.init_by(Mutex::new(ROOT_DIR.clone())); - *CURRENT_DIR_PATH.lock() = AbsPath::new("/"); + *CURRENT_DIR.lock() = AbsPath::new("/"); } From aa6577acdd11ac9862372e0d386c45ce6586a14c Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 27 Nov 2024 20:53:45 +0800 Subject: [PATCH 23/36] fix: path lifetime --- crates/axfs_vfs/src/path.rs | 70 +++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index a430ff769..f4ae13f9b 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -10,11 +10,13 @@ //! Utilities for path manipulation. use alloc::{ - borrow::{Cow, ToOwned}, format, string::String + borrow::{Cow, ToOwned}, + format, + string::{String, ToString}, }; /// Canonicalized absolute path type. -/// +/// /// - Starting with `/` /// - No `.` or `..` components /// - No redundant or tailing `/` @@ -24,23 +26,16 @@ use alloc::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct AbsPath<'a>(Cow<'a, str>); -impl<'a> AbsPath<'a> { - /// Simply wrap a str slice into a `AbsPath`. - /// - /// Caller should ensure that the path is absolute and canonicalized. - pub const fn new(path: &'a str) -> Self { - Self(Cow::Borrowed(path)) - } - +impl AbsPath<'static> { /// Simply wrap a string into a `AbsPath`. - /// + /// /// Caller should ensure that the path is absolute and canonicalized. pub const fn new_owned(path: String) -> Self { Self(Cow::Owned(path)) } /// Parse and canonicalize an absolute path from a string. - /// + /// /// - If the given path is not canonicalized, it will be canonicalized. /// - If the given path is not absolute, it will be prefixed with `/`. pub fn new_canonicalized(path: &str) -> Self { @@ -50,15 +45,29 @@ impl<'a> AbsPath<'a> { Self(Cow::Owned(canonicalize(path))) } } +} + +impl<'a> AbsPath<'a> { + /// Simply wrap a str slice into a `AbsPath`. + /// + /// Caller should ensure that the path is absolute and canonicalized. + pub const fn new(path: &'a str) -> Self { + Self(Cow::Borrowed(path)) + } /// Trim the starting `/` to transform this `AbsPath` into a `RelPath`. pub fn to_rel(&self) -> RelPath { RelPath(Cow::Borrowed(self.0.trim_start_matches('/'))) } + /// Create a new `AbsPath` with 'static lifetime. + pub fn to_owned(&self) -> AbsPath<'static> { + AbsPath::new_owned(self.0.to_string()) + } + /// Concatenate a `RelPath` to this `AbsPath`. - pub fn join(&self, rel: &RelPath) -> Self { - Self::new_canonicalized(&format!("{}/{}", self.0, rel.0)) + pub fn join(&self, rel: &RelPath) -> AbsPath<'static> { + AbsPath::new_canonicalized(&format!("{}/{}", self.0, rel.0)) } } @@ -77,7 +86,7 @@ impl core::fmt::Display for AbsPath<'_> { } /// Canonicalized relative path type. -/// +/// /// - No starting '/' /// - No `.` components /// - No redundant or tailing '/' @@ -88,28 +97,37 @@ impl core::fmt::Display for AbsPath<'_> { #[derive(Debug, Clone, PartialEq, Eq)] pub struct RelPath<'a>(Cow<'a, str>); +impl RelPath<'static> { + /// Simply wrap a string into a `RelPath`. + /// + /// Caller should ensure that the path is relative and canonicalized. + pub const fn new_owned(path: String) -> Self { + Self(Cow::Owned(path)) + } + + /// Parse and canonicalize a relative path from a string. + /// + /// - If the given path is not canonicalized, it will be canonicalized. + /// - If the given path is absolute, the starting '/' will be trimmed. + pub fn new_canonicalized(path: &str) -> Self { + Self(Cow::Owned(canonicalize(path.trim_start_matches('/')))) + } +} + impl<'a> RelPath<'a> { /// Simply wrap a string into a `RelPath`. - /// + /// /// Caller should ensure that the path is relative and canonicalized. pub const fn new(path: &'a str) -> Self { Self(Cow::Borrowed(path)) } /// Wrap a string into a `RelPath` with possibly leading '/' trimmed. - /// + /// /// Caller should ensure that the path is canonicalized. pub fn new_trimmed(path: &'a str) -> Self { Self(Cow::Borrowed(path.trim_start_matches('/'))) } - - /// Parse and canonicalize a relative path from a string. - /// - /// - If the given path is not canonicalized, it will be canonicalized. - /// - If the given path is absolute, the starting '/' will be trimmed. - pub fn new_canonicalized(path: &str) -> Self { - Self(Cow::Owned(canonicalize(path.trim_start_matches('/')))) - } } impl core::ops::Deref for RelPath<'_> { @@ -148,7 +166,7 @@ fn canonicalize(path: &str) -> String { match part { "" | "." => continue, ".." => { - if !is_absolute && buf.is_empty(){ + if !is_absolute && buf.is_empty() { buf.push_str(".."); continue; } From 5ea965fef0fd7e57b432bbfcf9ab5e307aae2e45 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 27 Nov 2024 20:59:49 +0800 Subject: [PATCH 24/36] refactor: filelike path method --- modules/ruxfdtable/Cargo.toml | 1 + modules/ruxfdtable/src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/modules/ruxfdtable/Cargo.toml b/modules/ruxfdtable/Cargo.toml index e918c39b1..75313c5f9 100644 --- a/modules/ruxfdtable/Cargo.toml +++ b/modules/ruxfdtable/Cargo.toml @@ -15,6 +15,7 @@ default = [] log = "0.4" spin = "0.9" axio = { path = "../../crates/axio" } +axfs_vfs = { path = "../../crates/axfs_vfs" } lazy_static = { version = "1.4", features = ["spin_no_std"] } flatten_objects = { path = "../../crates/flatten_objects" } axerrno = { path = "../../crates/axerrno" } diff --git a/modules/ruxfdtable/src/lib.rs b/modules/ruxfdtable/src/lib.rs index 275ef96f2..d9e35ed1a 100644 --- a/modules/ruxfdtable/src/lib.rs +++ b/modules/ruxfdtable/src/lib.rs @@ -16,6 +16,7 @@ use core::marker::Sync; use axerrno::LinuxResult; use axio::PollState; +use axfs_vfs::AbsPath; use flatten_objects::FlattenObjects; use spin::RwLock; @@ -100,6 +101,9 @@ pub struct RuxStat { /// Trait for file-like objects in a file descriptor table. pub trait FileLike: Send + Sync { + /// Get the absolute path of the file-like object. + fn path(&self) -> AbsPath; + /// Reads data from the file-like object into the provided buffer. /// /// Returns the number of bytes read on success. @@ -125,6 +129,7 @@ pub trait FileLike: Send + Sync { /// Sets or clears the non-blocking I/O mode for the file-like object. fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult; } + /// Maximum number of files per process pub const RUX_FILE_LIMIT: usize = 1024; From b48fff3b22ce0cf88414ba331fc6a7b22bd39cc9 Mon Sep 17 00:00:00 2001 From: liujingx Date: Wed, 27 Nov 2024 21:32:13 +0800 Subject: [PATCH 25/36] refactor: fs posix api with absolute path --- Cargo.lock | 1 + api/ruxos_posix_api/src/imp/fs.rs | 380 ++++++++++----------------- api/ruxos_posix_api/src/imp/stdio.rs | 9 + modules/ruxfs/src/fops.rs | 9 +- modules/ruxfs/src/fs/fatfs.rs | 3 +- 5 files changed, 154 insertions(+), 248 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78c904335..a01684d82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1663,6 +1663,7 @@ name = "ruxfdtable" version = "0.1.0" dependencies = [ "axerrno", + "axfs_vfs", "axio", "flatten_objects", "lazy_static", diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index aef50e794..dbc869271 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -7,7 +7,7 @@ * See the Mulan PSL v2 for more details. */ -use alloc::{string::ToString, sync::Arc}; +use alloc::sync::Arc; use core::{ ffi::{c_char, c_int, c_long, c_void, CStr}, str, @@ -50,6 +50,10 @@ impl File { } impl FileLike for File { + fn path(&self) -> AbsPath { + self.inner.lock().path().to_owned() + } + fn read(&self, buf: &mut [u8]) -> LinuxResult { Ok(self.inner.lock().read(buf)?) } @@ -128,6 +132,10 @@ impl Directory { } impl FileLike for Directory { + fn path(&self) -> AbsPath { + self.inner.lock().path().to_owned() + } + fn read(&self, _buf: &mut [u8]) -> LinuxResult { Err(LinuxError::EACCES) } @@ -188,17 +196,14 @@ fn flags_to_cap(flags: u32) -> Cap { /// Return its index in the file table (`fd`). Return `EMFILE` if it already /// has the maximum number of files open. pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let path = char_ptr_to_path(filename); - let flags = flags as u32; - debug!("sys_open <= {:?} {:#o} {:#o}", filename, flags, mode); - syscall_body!(sys_open, { - let path = path?; + let path = parse_path(filename)?; + let flags = flags as u32; + debug!("sys_open <= {:?} {:#o} {:#o}", path, flags, mode); // Check flag and attr - let node = match fops::lookup(&path.to_abs()) { + let node = match fops::lookup(&path) { Ok(node) => { if flags & ctypes::O_EXCL != 0 { - debug!("exist {} {}", flags, ctypes::O_EXCL); return Err(LinuxError::EEXIST); } node @@ -207,8 +212,8 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> if !(flags & ctypes::O_CREAT != 0) { return Err(LinuxError::ENOENT); } - fops::create_file(&path.to_abs())?; - fops::lookup(&path.to_abs())? + fops::create_file(&path)?; + fops::lookup(&path)? } Err(e) => return Err(e.into()), }; @@ -221,34 +226,23 @@ pub fn sys_open(filename: *const c_char, flags: c_int, mode: ctypes::mode_t) -> } // Open let append = flags & ctypes::O_APPEND != 0; - let file = fops::open_file(node, flags_to_cap(flags), append)?; + let file = fops::open_file(&path, node, flags_to_cap(flags), append)?; File::new(file).add_to_fd_table() }) } /// Open a file under a specific dir pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mode_t) -> c_int { - let path = char_ptr_to_path(path); - let flags = flags as u32; - let cap = flags_to_cap(flags); - debug!( - "sys_openat <= {}, {:?}, {:#o}, {:#o}", - fd, path, flags, mode - ); - syscall_body!(sys_openat, { - let path = path?; - let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; - // Get child node - let lookup_res = if absolute { - fops::lookup(&path.to_abs()) - } else { - let dir = Directory::from_fd(fd)?; - let node = dir.inner.lock().lookup(&path.to_rel()); - node - }; + let path = parse_path_at(fd, path)?; + let flags = flags as u32; + let cap = flags_to_cap(flags); + debug!( + "sys_openat <= {}, {:?}, {:#o}, {:#o}", + fd, path, flags, mode + ); // Check node attributes and handle not found - let node = match lookup_res { + let node = match fops::lookup(&path) { Ok(node) => { let attr = node.get_attr()?; // Node exists but O_EXCL is set @@ -272,42 +266,19 @@ pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mo return Err(LinuxError::ENOENT); } // Create file - if absolute { - let path = path.to_abs(); - fops::create_file(&path)?; - fops::lookup(&path)? - } else { - let path = path.to_rel(); - let dir = Directory::from_fd(fd)?; - dir.inner.lock().create_file(&path)?; - let node = dir.inner.lock().lookup(&path)?; - node - } + fops::create_file(&path)?; + fops::lookup(&path)? } Err(e) => return Err(e.into()), }; // Open file or directory let append = flags & ctypes::O_APPEND != 0; - match (absolute, node.get_attr()?.is_dir()) { - (true, true) => { - let dir = fops::open_dir(node, cap)?; - Directory::new(dir).add_to_fd_table() - } - (true, false) => { - let file = fops::open_file(node, cap, append)?; - File::new(file).add_to_fd_table() - } - (false, true) => { - let dir = Directory::from_fd(fd)?.inner.lock().open_dir(node, cap)?; - Directory::new(dir).add_to_fd_table() - } - (false, false) => { - let file = Directory::from_fd(fd)? - .inner - .lock() - .open_file(node, cap, append)?; - File::new(file).add_to_fd_table() - } + if node.get_attr()?.is_dir() { + let dir = fops::open_dir(&path, node, cap)?; + Directory::new(dir).add_to_fd_table() + } else { + let file = fops::open_file(&path, node, cap, append)?; + File::new(file).add_to_fd_table() } }) } @@ -321,8 +292,8 @@ pub fn sys_pread64( count: usize, pos: ctypes::off_t, ) -> ctypes::ssize_t { - debug!("sys_pread64 <= {} {} {}", fd, count, pos); syscall_body!(sys_pread64, { + debug!("sys_pread64 <= {} {} {}", fd, count, pos); if buf.is_null() { return Err(LinuxError::EFAULT); } @@ -341,8 +312,8 @@ pub fn sys_pwrite64( count: usize, pos: ctypes::off_t, ) -> ctypes::ssize_t { - debug!("sys_pwrite64 <= {} {} {}", fd, count, pos); syscall_body!(sys_pwrite64, { + debug!("sys_pwrite64 <= {} {} {}", fd, count, pos); if buf.is_null() { return Err(LinuxError::EFAULT); } @@ -356,8 +327,8 @@ pub fn sys_pwrite64( /// /// Return its position after seek. pub fn sys_lseek(fd: c_int, offset: ctypes::off_t, whence: c_int) -> ctypes::off_t { - debug!("sys_lseek <= {} {} {}", fd, offset, whence); syscall_body!(sys_lseek, { + debug!("sys_lseek <= {} {} {}", fd, offset, whence); let pos = match whence { 0 => SeekFrom::Start(offset as _), 1 => SeekFrom::Current(offset as _), @@ -389,19 +360,19 @@ pub unsafe fn sys_fdatasync(fd: c_int) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_int { - let path = char_ptr_to_path(path); - debug!("sys_stat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_stat, { + let path = parse_path(path)?; + debug!("sys_stat <= {:?} {:#x}", path, buf as usize); if buf.is_null() { return Err(LinuxError::EFAULT); } - let node = fops::lookup(&path?.to_abs())?; + let node = fops::lookup(&path)?; let attr = node.get_attr()?; let st = if attr.is_dir() { - let dir = fops::open_dir(node, Cap::READ)?; + let dir = fops::open_dir(&path, node, Cap::empty())?; Directory::new(dir).stat()?.into() } else { - let file = fops::open_file(node, Cap::READ, false)?; + let file = fops::open_file(&path, node, Cap::empty(), false)?; File::new(file).stat()?.into() }; @@ -433,8 +404,8 @@ pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_in /// retrieve information about the file pointed by `fd` pub fn sys_fstat(fd: c_int, kst: *mut core::ffi::c_void) -> c_int { - debug!("sys_fstat <= {} {:#x}", fd, kst as usize); syscall_body!(sys_fstat, { + debug!("sys_fstat <= {} {:#x}", fd, kst as usize); if kst.is_null() { return Err(LinuxError::EFAULT); } @@ -475,9 +446,9 @@ pub fn sys_fstat(fd: c_int, kst: *mut core::ffi::c_void) -> c_int { /// /// Return 0 if success. pub unsafe fn sys_lstat(path: *const c_char, buf: *mut ctypes::stat) -> ctypes::ssize_t { - let path = char_ptr_to_path(path); - debug!("sys_lstat <= {:?} {:#x}", path, buf as usize); syscall_body!(sys_lstat, { + let path = parse_path(path)?; + debug!("sys_lstat <= {:?} {:#x}", path, buf as usize); if buf.is_null() { return Err(LinuxError::EFAULT); } @@ -493,29 +464,20 @@ pub unsafe fn sys_newfstatat( kst: *mut ctypes::kstat, flag: c_int, ) -> c_int { - let path = char_ptr_to_path(path); - debug!( - "sys_newfstatat <= fd: {}, path: {:?}, flag: {:x}", - fd, path, flag - ); syscall_body!(sys_newfstatat, { - let path = path?; + let path = parse_path_at(fd, path)?; + debug!( + "sys_newfstatat <= fd: {}, path: {:?}, flag: {:x}", + fd, path, flag + ); if kst.is_null() { return Err(LinuxError::EFAULT); } - let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; - let node = if absolute { - fops::lookup(&path.to_abs())? - } else { - Directory::from_fd(fd)? - .inner - .lock() - .lookup(&path.to_rel())? - }; + let node = fops::lookup(&path)?; let st = if node.get_attr()?.is_dir() { - Directory::new(fops::open_dir(node, Cap::READ)?).stat()? + Directory::new(fops::open_dir(&path, node, Cap::empty())?).stat()? } else { - File::new(fops::open_file(node, Cap::READ, false)?).stat()? + File::new(fops::open_file(&path, node, Cap::empty(), false)?).stat()? }; unsafe { (*kst).st_dev = st.st_dev; @@ -558,40 +520,36 @@ pub fn sys_getcwd(buf: *mut c_char, size: usize) -> c_int { /// Return 0 if the operation succeeds, otherwise return -1. pub fn sys_rename(old: *const c_char, new: *const c_char) -> c_int { syscall_body!(sys_rename, { - let old_path = char_ptr_to_path(old)?; - let new_path = char_ptr_to_path(new)?; - debug!("sys_rename <= old: {:?}, new: {:?}", old_path, new_path); - if old_path == new_path { + let old = parse_path(old)?; + let new = parse_path(new)?; + debug!("sys_rename <= old: {:?}, new: {:?}", old, new); + if old == new { return Ok(0); } - match fops::lookup(&old_path.to_abs()) { + match fops::lookup(&old) { Ok(_) => {} Err(e) => return Err(e.into()), } - match fops::lookup(&new_path.to_abs()) { + match fops::lookup(&new) { Ok(_) => return Err(LinuxError::EEXIST), Err(Error::NotFound) => {} Err(e) => return Err(e.into()), } - fops::rename(&old_path.to_abs(), &new_path.to_abs())?; + fops::rename(&old, &new)?; Ok(0) }) } /// Rename at certain directory pointed by `oldfd` -/// -/// TODO: only support `oldfd`, `newfd` equals to AT_FDCWD pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const c_char) -> c_int { - let old_path = char_ptr_to_path(old); - let new_path = char_ptr_to_path(new); - debug!( - "sys_renameat <= oldfd: {}, old: {:?}, newfd: {}, new: {:?}", - oldfd, old_path, newfd, new_path - ); - assert_eq!(oldfd, ctypes::AT_FDCWD as c_int); - assert_eq!(newfd, ctypes::AT_FDCWD as c_int); syscall_body!(sys_renameat, { - fops::rename(&old_path?.to_abs(), &new_path?.to_abs())?; + let old_path = parse_path_at(oldfd, old)?; + let new_path = parse_path_at(newfd, new)?; + debug!( + "sys_renameat <= oldfd: {}, old: {:?}, newfd: {}, new: {:?}", + oldfd, old_path, newfd, new_path + ); + fops::rename(&old_path, &new_path)?; Ok(0) }) } @@ -599,9 +557,9 @@ pub fn sys_renameat(oldfd: c_int, old: *const c_char, newfd: c_int, new: *const /// Remove a directory, which must be empty pub fn sys_rmdir(pathname: *const c_char) -> c_int { syscall_body!(sys_rmdir, { - let path = char_ptr_to_path(pathname)?; + let path = parse_path(pathname)?; debug!("sys_rmdir <= path: {:?}", path); - match fops::lookup(&path.to_abs()) { + match fops::lookup(&path) { Ok(node) => { let attr = node.get_attr()?; if !attr.is_dir() { @@ -622,7 +580,7 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { return Err(LinuxError::ENOTEMPTY); } } - fops::remove_dir(&path.to_abs())?; + fops::remove_dir(&path)?; } Err(e) => return Err(e.into()), } @@ -633,9 +591,9 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { /// Removes a file from the filesystem. pub fn sys_unlink(pathname: *const c_char) -> c_int { syscall_body!(sys_unlink, { - let path = char_ptr_to_path(pathname)?; + let path = parse_path(pathname)?; debug!("sys_unlink <= path: {:?}", path); - match fops::lookup(&path.to_abs()) { + match fops::lookup(&path) { Ok(node) => { let attr = node.get_attr()?; if attr.is_dir() { @@ -644,7 +602,7 @@ pub fn sys_unlink(pathname: *const c_char) -> c_int { if !attr.perm().owner_writable() { return Err(LinuxError::EPERM); } - fops::remove_file(&path.to_abs())?; + fops::remove_file(&path)?; } Err(e) => return Err(e.into()), } @@ -654,22 +612,14 @@ pub fn sys_unlink(pathname: *const c_char) -> c_int { /// deletes a name from the filesystem pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { - debug!( - "sys_unlinkat <= fd: {}, pathname: {:?}, flags: {}", - fd, - char_ptr_to_path(pathname), - flags - ); - let rmdir = flags as u32 & ctypes::AT_REMOVEDIR != 0; syscall_body!(sys_unlinkat, { - let path = char_ptr_to_path(pathname)?; - let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; - let node = if absolute { - fops::lookup(&path.to_abs()) - } else { - Directory::from_fd(fd)?.inner.lock().lookup(&path.to_rel()) - }; - match node { + let path = parse_path(pathname)?; + let rmdir = flags as u32 & ctypes::AT_REMOVEDIR != 0; + debug!( + "sys_unlinkat <= fd: {}, pathname: {:?}, flags: {}", + fd, path, flags + ); + match fops::lookup(&path) { Ok(node) => { let attr = node.get_attr()?; if rmdir { @@ -691,14 +641,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { return Err(LinuxError::ENOTEMPTY); } } - if absolute { - fops::remove_dir(&path.to_abs())?; - } else { - Directory::from_fd(fd)? - .inner - .lock() - .unlink(&path.to_rel())?; - } + fops::remove_dir(&path)?; } else { if attr.is_dir() { return Err(LinuxError::EISDIR); @@ -706,14 +649,7 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { if !attr.perm().owner_writable() { return Err(LinuxError::EPERM); } - if absolute { - fops::remove_file(&path.to_abs())?; - } else { - Directory::from_fd(fd)? - .inner - .lock() - .unlink(&path.to_rel())?; - } + fops::remove_file(&path)?; } } Err(e) => return Err(e.into()), @@ -726,12 +662,12 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { // TODO: implement mode syscall_body!(sys_mkdir, { - let path = char_ptr_to_path(pathname)?; + let path = parse_path(pathname)?; debug!("sys_mkdir <= path: {:?}, mode: {:?}", path, mode); - let node = fops::lookup(&path.to_abs()); + let node = fops::lookup(&path); match node { Ok(_) => return Err(LinuxError::EEXIST), - Err(Error::NotFound) => fops::create_dir(&path.to_abs())?, + Err(Error::NotFound) => fops::create_dir(&path)?, Err(e) => return Err(e.into()), } Ok(0) @@ -740,32 +676,15 @@ pub fn sys_mkdir(pathname: *const c_char, mode: ctypes::mode_t) -> c_int { /// attempts to create a directory named pathname under directory pointed by `fd` pub fn sys_mkdirat(fd: c_int, pathname: *const c_char, mode: ctypes::mode_t) -> c_int { - debug!( - "sys_mkdirat <= fd: {}, pathname: {:?}, mode: {:x?}", - fd, - char_ptr_to_path(pathname), - mode - ); syscall_body!(sys_mkdirat, { - let path = char_ptr_to_path(pathname)?; - let absolute = matches!(path, Path::Absolute(_)) || fd == ctypes::AT_FDCWD; - let node = if absolute { - fops::lookup(&path.to_abs()) - } else { - Directory::from_fd(fd)?.inner.lock().lookup(&path.to_rel()) - }; - match node { + let path = parse_path_at(fd, pathname)?; + debug!( + "sys_mkdirat <= fd: {}, pathname: {:?}, mode: {:x?}", + fd, path, mode + ); + match fops::lookup(&path) { Ok(_) => return Err(LinuxError::EEXIST), - Err(Error::NotFound) => { - if absolute { - fops::create_dir(&path.to_abs())?; - } else { - Directory::from_fd(fd)? - .inner - .lock() - .create_dir(&path.to_rel())?; - } - } + Err(Error::NotFound) => fops::create_dir(&path)?, Err(e) => return Err(e.into()), } Ok(0) @@ -780,15 +699,14 @@ pub fn sys_fchownat( gid: ctypes::gid_t, flag: c_int, ) -> c_int { - debug!( - "sys_fchownat <= fd: {}, path: {:?}, uid: {}, gid: {}, flag: {}", - fd, - char_ptr_to_path(path), - uid, - gid, - flag - ); - syscall_body!(sys_fchownat, Ok(0)) + syscall_body!(sys_fchownat, { + let path = parse_path_at(fd, path)?; + debug!( + "sys_fchownat <= fd: {}, path: {:?}, uid: {}, gid: {}, flag: {}", + fd, path, uid, gid, flag + ); + Ok(0) + }) } /// read value of a symbolic link relative to directory file descriptor @@ -799,12 +717,12 @@ pub fn sys_readlinkat( buf: *mut c_char, bufsize: usize, ) -> usize { - let path = char_ptr_to_path(pathname); - debug!( - "sys_readlinkat <= path = {:?}, fd = {:}, buf = {:p}, bufsize = {:}", - path, fd, buf, bufsize - ); syscall_body!(sys_readlinkat, { + let path = parse_path_at(fd, pathname)?; + debug!( + "sys_readlinkat <= path = {:?}, fd = {:}, buf = {:p}, bufsize = {:}", + path, fd, buf, bufsize + ); Err::(LinuxError::EINVAL) }) } @@ -814,14 +732,11 @@ type LinuxDirent64 = ctypes::dirent; const DIRENT64_FIXED_SIZE: usize = 19; /// Read directory entries from a directory file descriptor. -/// -/// TODO: check errors, change 280 to a special value pub unsafe fn sys_getdents64(fd: c_int, dirp: *mut LinuxDirent64, count: ctypes::size_t) -> c_long { debug!( "sys_getdents64 <= fd: {}, dirp: {:p}, count: {}", fd, dirp, count ); - syscall_body!(sys_getdents64, { if count < DIRENT64_FIXED_SIZE { return Err(LinuxError::EINVAL); @@ -912,12 +827,12 @@ pub unsafe fn sys_preadv( /// The mode is either the value F_OK, for the existence of the file, /// or a mask consisting of the bitwise OR of one or more of R_OK, W_OK, and X_OK, for the read, write, execute permissions. pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: c_int) -> c_int { - let path = char_ptr_to_path(pathname).unwrap(); - debug!( - "sys_faccessat <= dirfd {} path {} mode {} flags {}", - dirfd, path, mode, flags - ); syscall_body!(sys_faccessat, { + let path = parse_path_at(dirfd, pathname)?; + debug!( + "sys_faccessat <= dirfd {} path {} mode {} flags {}", + dirfd, path, mode, flags + ); // TODO: dirfd // let mut options = OpenOptions::new(); // options.read(true); @@ -928,55 +843,14 @@ pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: /// changes the current working directory to the directory specified in path. pub fn sys_chdir(path: *const c_char) -> c_int { - let path = char_ptr_to_path(path); - debug!("sys_chdir <= path: {:?}", path); syscall_body!(sys_chdir, { - let path = path?; - fops::set_current_dir(AbsPath::new_owned(path.to_abs().to_string()))?; + let path = parse_path(path)?; + debug!("sys_chdir <= path: {:?}", path); + fops::set_current_dir(path)?; Ok(0) }) } -/// Generic path type. -#[derive(Debug, PartialEq)] -enum Path<'a> { - Absolute(AbsPath<'a>), - Relative(RelPath<'a>), -} - -impl<'a> Path<'a> { - /// Translate the path into a `RelPath`. - /// - /// * If the path is already a relative path, it is returned as is. - /// * If the path is an absolute path, its root is stripped. - pub fn to_rel(&'a self) -> RelPath<'a> { - match self { - Path::Absolute(p) => p.to_rel(), - Path::Relative(p) => p.clone(), - } - } - - /// Translate the path into a `AbsPath`. - /// - /// * If the path is already an absolute path, it is returned as is. - /// * If the path is a relative path, it is resolved against the current working directory. - pub fn to_abs(&'a self) -> AbsPath<'a> { - match self { - Path::Absolute(p) => p.clone(), - Path::Relative(p) => fops::current_dir().join(&p), - } - } -} - -impl core::fmt::Display for Path<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Path::Absolute(p) => write!(f, "{}", p), - Path::Relative(p) => write!(f, "{}", p), - } - } -} - /// from char_ptr get path_str fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { if ptr.is_null() { @@ -988,12 +862,34 @@ fn char_ptr_to_path_str<'a>(ptr: *const c_char) -> LinuxResult<&'a str> { } } -/// from char_ptr get wrapped path type -fn char_ptr_to_path<'a>(ptr: *const c_char) -> LinuxResult> { - let path = char_ptr_to_path_str(ptr)?; +/// Parse `path` argument for fs syscalls. +/// +/// * If the given `path` is absolute, return it as is. +/// * If the given `path` is relative, join it against the current working directory. +pub fn parse_path(path: *const c_char) -> LinuxResult> { + let path = char_ptr_to_path_str(path)?; + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) + } else { + Ok(fops::current_dir().join(&RelPath::new_canonicalized(path))) + } +} + +/// Parse `path` and `dirfd` arguments for fs syscalls. +/// +/// * If the given `path` is absolute, return it as is. +/// * If the given `path` is relative and `dirfd` is `AT_FDCWD`, join it against the +/// current working directory. +/// * If the given `path` is relative and `dirfd` is not `AT_FDCWD`, join it against the +/// directory of the file descriptor. +pub fn parse_path_at(dirfd: c_int, path: *const c_char) -> LinuxResult> { + let path = char_ptr_to_path_str(path)?; if path.starts_with('/') { - Ok(Path::Absolute(AbsPath::new_canonicalized(path))) + Ok(AbsPath::new_canonicalized(path)) + } else if dirfd == ctypes::AT_FDCWD { + Ok(fops::current_dir().join(&RelPath::new_canonicalized(path))) } else { - Ok(Path::Relative(RelPath::new_canonicalized(path))) + let dir = Directory::from_fd(dirfd)?; + Ok(dir.path().join(&RelPath::new_canonicalized(path))) } } diff --git a/api/ruxos_posix_api/src/imp/stdio.rs b/api/ruxos_posix_api/src/imp/stdio.rs index 73af31e31..6407592fc 100644 --- a/api/ruxos_posix_api/src/imp/stdio.rs +++ b/api/ruxos_posix_api/src/imp/stdio.rs @@ -10,6 +10,7 @@ use axerrno::AxResult; use axio::{prelude::*, BufReader}; use axsync::Mutex; +use ruxfs::AbsPath; #[cfg(feature = "fd")] use { @@ -130,6 +131,10 @@ pub fn stdout() -> Stdout { #[cfg(feature = "fd")] impl ruxfdtable::FileLike for Stdin { + fn path(&self) -> AbsPath { + AbsPath::new("/dev/stdin") + } + fn read(&self, buf: &mut [u8]) -> LinuxResult { match self.nonblocking.load(Ordering::Relaxed) { true => Ok(self.read_nonblocked(buf)?), @@ -174,6 +179,10 @@ impl ruxfdtable::FileLike for Stdin { #[cfg(feature = "fd")] impl ruxfdtable::FileLike for Stdout { + fn path(&self) -> AbsPath { + AbsPath::new("/dev/stdout") + } + fn read(&self, _buf: &mut [u8]) -> LinuxResult { Err(LinuxError::EPERM) } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 5bda98ac2..566abbb1b 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -9,7 +9,6 @@ //! Low-level filesystem operations. -use alloc::borrow::ToOwned; use axerrno::{ax_err, ax_err_type, AxResult}; use axfs_vfs::{AbsPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; use axio::SeekFrom; @@ -51,8 +50,8 @@ impl File { } /// Get the abcolute path of the file. - pub fn path(&self) -> &AbsPath { - &self.path + pub fn path(&self) -> AbsPath { + self.path.clone() } /// Gets the file attributes. @@ -144,8 +143,8 @@ impl Directory { } /// Gets the absolute path of the directory. - pub fn path(&self) -> &AbsPath { - &self.path + pub fn path(&self) -> AbsPath { + self.path.clone() } /// Gets the file attributes. diff --git a/modules/ruxfs/src/fs/fatfs.rs b/modules/ruxfs/src/fs/fatfs.rs index 15822b493..3f6d5eb68 100644 --- a/modules/ruxfs/src/fs/fatfs.rs +++ b/modules/ruxfs/src/fs/fatfs.rs @@ -85,7 +85,7 @@ impl VfsNodeOps for FileWrapper<'static> { let blocks = (size + BLOCK_SIZE as u64 - 1) / BLOCK_SIZE as u64; // FAT fs doesn't support permissions, we just set everything to 755 let perm = VfsNodePerm::from_bits_truncate(0o755); - Ok(VfsNodeAttr::new(perm, VfsNodeType::File, size, blocks)) + Ok(VfsNodeAttr::new(0, perm, VfsNodeType::File, size, blocks)) } fn read_at(&self, offset: u64, buf: &mut [u8]) -> VfsResult { @@ -133,6 +133,7 @@ impl VfsNodeOps for DirWrapper<'static> { fn get_attr(&self) -> VfsResult { // FAT fs doesn't support permissions, we just set everything to 755 Ok(VfsNodeAttr::new( + 0, VfsNodePerm::from_bits_truncate(0o755), VfsNodeType::Dir, BLOCK_SIZE as u64, From b41482f4df980dc34b49cc939e980eb08def5ec3 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 12:09:05 +0800 Subject: [PATCH 26/36] fix: FileLike stat --- api/ruxos_posix_api/src/imp/fs.rs | 60 +++++++------------------------ modules/ruxfdtable/src/lib.rs | 32 +++++++++++++++++ 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index dbc869271..0da01abaf 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -67,29 +67,7 @@ impl FileLike for File { } fn stat(&self) -> LinuxResult { - let metadata = self.inner.lock().get_attr()?; - let ty = metadata.file_type() as u8; - let perm = metadata.perm().bits() as u32; - let st_mode = ((ty as u32) << 12) | perm; - - // Inode of files, for musl dynamic linker. - // WARN: there will be collision for files with the same size. - // TODO: implement real inode. - let st_ino = metadata.size() + st_mode as u64; - - let res = RuxStat::from(ctypes::stat { - st_ino, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_size: metadata.size() as _, - st_blocks: metadata.blocks() as _, - st_blksize: 512, - ..Default::default() - }); - - Ok(res) + self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)) } fn into_any(self: Arc) -> Arc { @@ -110,12 +88,14 @@ impl FileLike for File { pub struct Directory { inner: Mutex, + searchable: bool, } impl Directory { - fn new(inner: ruxfs::fops::Directory) -> Self { + fn new(inner: ruxfs::fops::Directory, searchable: bool) -> Self { Self { inner: Mutex::new(inner), + searchable, } } @@ -149,21 +129,7 @@ impl FileLike for Directory { } fn stat(&self) -> LinuxResult { - let metadata = self.inner.lock().get_attr()?; - let ty = metadata.file_type() as u8; - let perm = metadata.perm().bits() as u32; - let st_mode = ((ty as u32) << 12) | perm; - Ok(RuxStat::from(ctypes::stat { - st_ino: 1, - st_nlink: 1, - st_mode, - st_uid: 1000, - st_gid: 1000, - st_size: metadata.size() as _, - st_blocks: metadata.blocks() as _, - st_blksize: 512, - ..Default::default() - })) + self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)) } fn into_any(self: Arc) -> Arc { @@ -275,7 +241,7 @@ pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mo let append = flags & ctypes::O_APPEND != 0; if node.get_attr()?.is_dir() { let dir = fops::open_dir(&path, node, cap)?; - Directory::new(dir).add_to_fd_table() + Directory::new(dir, (flags & ctypes::O_SEARCH != 0)).add_to_fd_table() } else { let file = fops::open_file(&path, node, cap, append)?; File::new(file).add_to_fd_table() @@ -370,7 +336,7 @@ pub unsafe fn sys_stat(path: *const c_char, buf: *mut core::ffi::c_void) -> c_in let attr = node.get_attr()?; let st = if attr.is_dir() { let dir = fops::open_dir(&path, node, Cap::empty())?; - Directory::new(dir).stat()?.into() + Directory::new(dir, false).stat()?.into() } else { let file = fops::open_file(&path, node, Cap::empty(), false)?; File::new(file).stat()?.into() @@ -475,7 +441,7 @@ pub unsafe fn sys_newfstatat( } let node = fops::lookup(&path)?; let st = if node.get_attr()?.is_dir() { - Directory::new(fops::open_dir(&path, node, Cap::empty())?).stat()? + Directory::new(fops::open_dir(&path, node, Cap::empty())?, false).stat()? } else { File::new(fops::open_file(&path, node, Cap::empty(), false)?).stat()? }; @@ -833,10 +799,6 @@ pub fn sys_faccessat(dirfd: c_int, pathname: *const c_char, mode: c_int, flags: "sys_faccessat <= dirfd {} path {} mode {} flags {}", dirfd, path, mode, flags ); - // TODO: dirfd - // let mut options = OpenOptions::new(); - // options.read(true); - // let _file = options.open(path)?; Ok(0) }) } @@ -890,6 +852,10 @@ pub fn parse_path_at(dirfd: c_int, path: *const c_char) -> LinuxResult for RuxStat { + fn from(attr: VfsNodeAttr) -> Self { + Self { + st_dev: 0, + st_ino: attr.ino(), + st_nlink: 1, + st_mode: ((attr.file_type() as u32) << 12) | attr.perm().bits() as u32, + st_uid: 1000, + st_gid: 1000, + __pad0: 0, + st_rdev: 0, + st_size: attr.size() as _, + st_blksize: 512, + st_blocks: attr.blocks() as _, + st_atime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_mtime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_ctime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + __unused: [0; 3], + } + } +} + /// Trait for file-like objects in a file descriptor table. pub trait FileLike: Send + Sync { /// Get the absolute path of the file-like object. From cb5165b6be5e226c21fef0bb387dee3248362f7b Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 12:26:11 +0800 Subject: [PATCH 27/36] feat: directory check empty --- api/ruxos_posix_api/src/imp/fs.rs | 32 +++++++------------------------ crates/axfs_vfs/src/lib.rs | 12 ++++++++++++ 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index 0da01abaf..c91e6c6e5 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -67,7 +67,7 @@ impl FileLike for File { } fn stat(&self) -> LinuxResult { - self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)) + self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)).map_err(|e| e.into()) } fn into_any(self: Arc) -> Arc { @@ -129,7 +129,7 @@ impl FileLike for Directory { } fn stat(&self) -> LinuxResult { - self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)) + self.inner.lock().get_attr().map(|attr| RuxStat::from(attr)).map_err(|e| e.into()) } fn into_any(self: Arc) -> Arc { @@ -241,7 +241,7 @@ pub fn sys_openat(fd: c_int, path: *const c_char, flags: c_int, mode: ctypes::mo let append = flags & ctypes::O_APPEND != 0; if node.get_attr()?.is_dir() { let dir = fops::open_dir(&path, node, cap)?; - Directory::new(dir, (flags & ctypes::O_SEARCH != 0)).add_to_fd_table() + Directory::new(dir, flags & ctypes::O_SEARCH != 0).add_to_fd_table() } else { let file = fops::open_file(&path, node, cap, append)?; File::new(file).add_to_fd_table() @@ -534,17 +534,8 @@ pub fn sys_rmdir(pathname: *const c_char) -> c_int { if !attr.perm().owner_writable() { return Err(LinuxError::EPERM); } - // Directory has no check_empty() method, so - // we uses a brute force way to check it. - let mut buf = [ - DirEntry::default(), - DirEntry::default(), - DirEntry::default(), - ]; - if let Ok(n) = node.read_dir(0, &mut buf) { - if n > 2 { - return Err(LinuxError::ENOTEMPTY); - } + if !node.is_empty()? { + return Err(LinuxError::ENOTEMPTY); } fops::remove_dir(&path)?; } @@ -595,17 +586,8 @@ pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { if !attr.perm().owner_writable() { return Err(LinuxError::EPERM); } - // Directory has no check_empty() method, so - // we uses a brute force way to check it. - let mut buf = [ - DirEntry::default(), - DirEntry::default(), - DirEntry::default(), - ]; - if let Ok(n) = node.read_dir(0, &mut buf) { - if n > 2 { - return Err(LinuxError::ENOTEMPTY); - } + if !node.is_empty()? { + return Err(LinuxError::ENOTEMPTY); } fops::remove_dir(&path)?; } else { diff --git a/crates/axfs_vfs/src/lib.rs b/crates/axfs_vfs/src/lib.rs index 44a0bd14d..ac8fe5b17 100644 --- a/crates/axfs_vfs/src/lib.rs +++ b/crates/axfs_vfs/src/lib.rs @@ -187,6 +187,18 @@ pub trait VfsNodeOps: Send + Sync { ax_err!(Unsupported) } + /// Check if the directory is empty. An empty directory only contains `.` and `..`. + /// + /// Brute implementation: read entries and check if there are more than 2. + fn is_empty(&self) -> VfsResult { + let mut buf = [ + VfsDirEntry::default(), + VfsDirEntry::default(), + VfsDirEntry::default(), + ]; + self.read_dir(0, &mut buf).map(|n| n <= 2) + } + /// Convert `&self` to [`&dyn Any`][1] that can use /// [`Any::downcast_ref`][2]. /// From 076bd58718cf5a39464efe95574f434e6dbf7d4a Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 14:06:39 +0800 Subject: [PATCH 28/36] fix: unlinkat --- api/ruxos_posix_api/src/imp/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/ruxos_posix_api/src/imp/fs.rs b/api/ruxos_posix_api/src/imp/fs.rs index c91e6c6e5..81f80efa0 100644 --- a/api/ruxos_posix_api/src/imp/fs.rs +++ b/api/ruxos_posix_api/src/imp/fs.rs @@ -570,7 +570,7 @@ pub fn sys_unlink(pathname: *const c_char) -> c_int { /// deletes a name from the filesystem pub fn sys_unlinkat(fd: c_int, pathname: *const c_char, flags: c_int) -> c_int { syscall_body!(sys_unlinkat, { - let path = parse_path(pathname)?; + let path = parse_path_at(fd, pathname)?; let rmdir = flags as u32 & ctypes::AT_REMOVEDIR != 0; debug!( "sys_unlinkat <= fd: {}, pathname: {:?}, flags: {}", From 33bf390ea497c8e4b42538d4f57bf2c713bc6423 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 15:56:25 +0800 Subject: [PATCH 29/36] Update ruxfs dependencies --- Cargo.lock | 1 + modules/ruxfs/Cargo.toml | 2 +- modules/ruxfs/src/fs/another_ext4.rs | 4 ++++ modules/ruxfs/src/fs/ext4_rs.rs | 4 ++++ modules/ruxfs/src/fs/lwext4_rust.rs | 6 +++++- 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a01684d82..1e3756d7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1146,6 +1146,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lwext4_rust" version = "0.2.0" +source = "git+https://github.com/elliott10/lwext4_rust#83489293ef4535672caee0c5755729f3e0315a96" dependencies = [ "log", "printf-compat", diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 5c4a24f55..107b964fe 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -44,7 +44,7 @@ axsync = { path = "../axsync" } crate_interface = { version = "0.1.1", optional = true } axalloc = { path = "../axalloc", optional = true } memory_addr = "0.1.0" -lwext4_rust = { path = "../../crates/lwext4_rust", optional = true } +lwext4_rust = { git = "https://github.com/elliott10/lwext4_rust", optional = true } ext4_rs = { git = "https://github.com/yuoo655/ext4_rs.git", rev= "6bcc7f5", optional = true } another_ext4 = { git = "https://github.com/LJxTHUCS/another_ext4.git", optional = true } diff --git a/modules/ruxfs/src/fs/another_ext4.rs b/modules/ruxfs/src/fs/another_ext4.rs index 0925b2771..e416a2582 100644 --- a/modules/ruxfs/src/fs/another_ext4.rs +++ b/modules/ruxfs/src/fs/another_ext4.rs @@ -1,3 +1,7 @@ +//! Reference: +//! - another_ext4: https://github.com/LJxTHUCS/another_ext4 +//! - axfs: https://github.com/Starry-OS/axfs + use crate::dev::Disk; use alloc::sync::Arc; use another_ext4::{ diff --git a/modules/ruxfs/src/fs/ext4_rs.rs b/modules/ruxfs/src/fs/ext4_rs.rs index 5b08440a0..4a67e0903 100644 --- a/modules/ruxfs/src/fs/ext4_rs.rs +++ b/modules/ruxfs/src/fs/ext4_rs.rs @@ -1,3 +1,7 @@ +//! Reference: +//! - ext4_rs: https://github.com/yuoo655/ext4_rs +//! - axfs: https://github.com/Starry-OS/axfs + use crate::dev::Disk; use alloc::sync::Arc; use alloc::vec; diff --git a/modules/ruxfs/src/fs/lwext4_rust.rs b/modules/ruxfs/src/fs/lwext4_rust.rs index e6f375279..ee5540e72 100644 --- a/modules/ruxfs/src/fs/lwext4_rust.rs +++ b/modules/ruxfs/src/fs/lwext4_rust.rs @@ -1,3 +1,7 @@ +//! Reference: +//! - lwext4_rust: https://github.com/elliott10/lwext4_rust +//! - axfs: https://github.com/Starry-OS/axfs + use crate::alloc::string::String; use alloc::sync::Arc; use axerrno::AxError; @@ -137,7 +141,7 @@ impl VfsNodeOps for FileWrapper { blocks ); - Ok(VfsNodeAttr::new(perm, vtype, size, blocks)) + Ok(VfsNodeAttr::new(0, perm, vtype, size, blocks)) } fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { From 7852ddbb5080547429d5be639f669f835866a1a5 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 16:18:27 +0800 Subject: [PATCH 30/36] fix: blkfs features --- api/ruxfeat/Cargo.toml | 2 +- modules/ruxfdtable/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++- modules/ruxfs/Cargo.toml | 1 + modules/ruxfs/src/lib.rs | 3 +++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/api/ruxfeat/Cargo.toml b/api/ruxfeat/Cargo.toml index c22522907..01e0a37de 100644 --- a/api/ruxfeat/Cargo.toml +++ b/api/ruxfeat/Cargo.toml @@ -47,7 +47,7 @@ sched_cfs = ["ruxtask/sched_cfs", "irq"] # File system fs = ["alloc", "dep:ruxfs", "ruxruntime/fs"] -blkfs = ["ruxdriver/virtio-blk", "ruxruntime/blkfs"] +blkfs = ["ruxdriver/virtio-blk", "ruxruntime/blkfs", "ruxfs/blkfs"] myfs = ["ruxfs?/myfs"] 9pfs = [] fatfs = ["ruxfs?/fatfs"] diff --git a/modules/ruxfdtable/src/lib.rs b/modules/ruxfdtable/src/lib.rs index 29fb3c842..76cca23c8 100644 --- a/modules/ruxfdtable/src/lib.rs +++ b/modules/ruxfdtable/src/lib.rs @@ -16,8 +16,8 @@ use core::marker::Send; use core::marker::Sync; use axerrno::LinuxResult; -use axio::PollState; use axfs_vfs::AbsPath; +use axio::PollState; use flatten_objects::FlattenObjects; use spin::RwLock; @@ -65,6 +65,7 @@ pub struct RuxStat { /// Unused space, reserved for future use. pub __unused: [core::ffi::c_uint; 2usize], } + ///Rust version for struct stat in ctypes. Represents file status information. #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] pub struct RuxStat { @@ -100,6 +101,40 @@ pub struct RuxStat { pub __unused: [core::ffi::c_long; 3usize], } +#[cfg(target_arch = "aarch64")] +impl From for RuxStat { + fn from(attr: VfsNodeAttr) -> Self { + Self { + st_dev: 0, + st_ino: attr.ino(), + st_nlink: 1, + st_mode: ((attr.file_type() as u32) << 12) | attr.perm().bits() as u32, + st_uid: 1000, + st_gid: 1000, + st_rdev: 0, + __pad: 0, + st_size: attr.size() as _, + st_blksize: 512, + __pad2: 0, + st_blocks: attr.blocks() as _, + st_atime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_mtime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + st_ctime: RuxTimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + __unused: [0; 2], + } + } +} + +#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] impl From for RuxStat { fn from(attr: VfsNodeAttr) -> Self { Self { diff --git a/modules/ruxfs/Cargo.toml b/modules/ruxfs/Cargo.toml index 107b964fe..a2c4e3ffa 100644 --- a/modules/ruxfs/Cargo.toml +++ b/modules/ruxfs/Cargo.toml @@ -17,6 +17,7 @@ ramfs = ["dep:axfs_ramfs"] procfs = ["dep:axfs_ramfs"] sysfs = ["dep:axfs_ramfs"] etcfs = ["dep:axfs_ramfs"] +blkfs = [] fatfs = ["dep:fatfs"] lwext4_rust = ["dep:lwext4_rust"] ext4_rs = ["dep:ext4_rs"] diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index b86a51e3d..2ecbbb561 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -79,6 +79,7 @@ pub fn init_tempfs() -> MountPoint { } /// Initializes filesystems by block devices. +#[cfg(feature = "blkfs")] pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint { info!("Initialize filesystems..."); @@ -106,6 +107,8 @@ pub fn init_blkfs(mut blk_devs: AxDeviceContainer) -> MountPoint static EXT4_FS: LazyInit> = LazyInit::new(); EXT4_FS.init_by(Arc::new(fs::another_ext4::Ext4FileSystem::new(disk))); let blk_fs = EXT4_FS.clone(); + } else { + compile_error!("Please enable one of the block filesystems!"); } } From a3f7b9a3e7e9442b271e8ff05bdf470379f967ed Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 20:46:49 +0800 Subject: [PATCH 31/36] feat: ruxfs high-level api --- crates/axfs_vfs/src/path.rs | 10 ++ crates/axfs_vfs/src/structs.rs | 7 ++ modules/ruxfs/src/api/dir.rs | 128 ++++++++---------------- modules/ruxfs/src/api/file.rs | 172 ++++++++++++--------------------- modules/ruxfs/src/api/mod.rs | 35 +++---- modules/ruxfs/src/fops.rs | 7 +- 6 files changed, 143 insertions(+), 216 deletions(-) diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index f4ae13f9b..a18daefa8 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -65,6 +65,16 @@ impl<'a> AbsPath<'a> { AbsPath::new_owned(self.0.to_string()) } + /// Transform this `AbsPath` into a raw str slice. + pub fn as_str(&self) -> &str { + self.0.as_ref() + } + + /// Transform this `AbsPath` into a raw string. + pub fn to_string(&self) -> String { + self.0.to_string() + } + /// Concatenate a `RelPath` to this `AbsPath`. pub fn join(&self, rel: &RelPath) -> AbsPath<'static> { AbsPath::new_canonicalized(&format!("{}/{}", self.0, rel.0)) diff --git a/crates/axfs_vfs/src/structs.rs b/crates/axfs_vfs/src/structs.rs index c1a4e9873..ae20688f9 100644 --- a/crates/axfs_vfs/src/structs.rs +++ b/crates/axfs_vfs/src/structs.rs @@ -76,7 +76,14 @@ pub enum VfsNodeType { Socket = 0o14, } +impl Default for VfsNodeType { + fn default() -> Self { + Self::File + } +} + /// Directory entry. +#[derive(Clone)] pub struct VfsDirEntry { d_type: VfsNodeType, d_name: [u8; 63], diff --git a/modules/ruxfs/src/api/dir.rs b/modules/ruxfs/src/api/dir.rs index c3852d6db..3356b4430 100644 --- a/modules/ruxfs/src/api/dir.rs +++ b/modules/ruxfs/src/api/dir.rs @@ -7,109 +7,55 @@ * See the Mulan PSL v2 for more details. */ -use alloc::string::String; +use alloc::{borrow::ToOwned, string::String, vec}; use axerrno::ax_err; use axfs_vfs::{AbsPath, VfsError}; use axio::Result; -use core::fmt; +use capability::Cap; +use core::{fmt, str}; -use super::{FileType, OpenOptions}; +use super::FileType; use crate::fops; /// Iterator over the entries in a directory. -pub struct ReadDir<'a> { - path: &'a AbsPath<'a>, +pub struct Directory { inner: fops::Directory, - buf_pos: usize, - buf_end: usize, - end_of_stream: bool, - dirent_buf: [fops::DirEntry; 31], } -/// Entries returned by the [`ReadDir`] iterator. -pub struct DirEntry<'a> { - dir_path: &'a str, - entry_name: String, - entry_type: FileType, -} - -/// A builder used to create directories in various manners. -#[derive(Default, Debug)] -pub struct DirBuilder { - recursive: bool, -} - -impl<'a> ReadDir<'a> { - pub(super) fn new(path: &'a AbsPath<'a>) -> Result { - let mut opts = OpenOptions::new(); - opts.read(true); - let node = fops::lookup(path)?; - let inner = fops::open_dir(path, node, (&opts).into())?; - const EMPTY: fops::DirEntry = fops::DirEntry::default(); - let dirent_buf = [EMPTY; 31]; - Ok(ReadDir { - path, - inner, - end_of_stream: false, - buf_pos: 0, - buf_end: 0, - dirent_buf, - }) +impl Directory { + /// Opens a directory for reading entries. + pub fn open(path: AbsPath<'static>) -> Result { + let node = fops::lookup(&path)?; + let inner = fops::open_dir(&path, node, Cap::EXECUTE)?; + Ok(Self { inner }) } -} - -impl<'a> Iterator for ReadDir<'a> { - type Item = Result>; - fn next(&mut self) -> Option>> { - if self.end_of_stream { - return None; - } - - loop { - if self.buf_pos >= self.buf_end { - match self.inner.read_dir(&mut self.dirent_buf) { - Ok(n) => { - if n == 0 { - self.end_of_stream = true; - return None; - } - self.buf_pos = 0; - self.buf_end = n; - } - Err(e) => { - self.end_of_stream = true; - return Some(Err(e)); - } - } - } - let entry = &self.dirent_buf[self.buf_pos]; - self.buf_pos += 1; - let name_bytes = entry.name_as_bytes(); - if name_bytes == b"." || name_bytes == b".." { - continue; - } - let entry_name = unsafe { core::str::from_utf8_unchecked(name_bytes).into() }; - let entry_type = entry.entry_type(); - - return Some(Ok(DirEntry { - dir_path: self.path, - entry_name, - entry_type, - })); + /// Reads directory entries starts from the current position into the + /// given buffer, returns the number of entries read. + /// + /// After the read, the cursor of the directory will be advanced by the + /// number of entries read. + pub fn read_dir(&mut self, buf: &mut [DirEntry]) -> Result { + let mut buffer = vec![fops::DirEntry::default(); buf.len()]; + let len = self.inner.read_dir(&mut buffer)?; + for (i, entry) in buffer.iter().enumerate().take(len) { + buf[i] = DirEntry { + entry_name: unsafe { str::from_utf8_unchecked(entry.name_as_bytes()).to_owned() }, + entry_type: entry.entry_type(), + }; } + Ok(len) } } -impl<'a> DirEntry<'a> { - /// Returns the full path to the file that this entry represents. - /// - /// The full path is created by joining the original path to `read_dir` - /// with the filename of this entry. - pub fn path(&self) -> String { - String::from(self.dir_path.trim_end_matches('/')) + "/" + &self.entry_name - } +/// Entry type used by `Directory::read_dir`. +#[derive(Default)] +pub struct DirEntry { + entry_name: String, + entry_type: FileType, +} +impl DirEntry { /// Returns the bare file name of this directory entry without any other /// leading path component. pub fn file_name(&self) -> String { @@ -122,12 +68,18 @@ impl<'a> DirEntry<'a> { } } -impl fmt::Debug for DirEntry<'_> { +impl fmt::Debug for DirEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("DirEntry").field(&self.path()).finish() + f.debug_tuple("DirEntry").field(&self.file_name()).finish() } } +/// A builder used to create directories in various manners. +#[derive(Default, Debug)] +pub struct DirBuilder { + recursive: bool, +} + impl DirBuilder { /// Creates a new set of options with default mode/security settings for all /// platforms and also non-recursive. diff --git a/modules/ruxfs/src/api/file.rs b/modules/ruxfs/src/api/file.rs index 1a464b688..15b947096 100644 --- a/modules/ruxfs/src/api/file.rs +++ b/modules/ruxfs/src/api/file.rs @@ -7,27 +7,23 @@ * See the Mulan PSL v2 for more details. */ -use crate::fops::{self, current_dir}; use axerrno::ax_err; -use axfs_vfs::{AbsPath, RelPath, VfsError}; +use axfs_vfs::{AbsPath, VfsError}; use axio::{prelude::*, Result, SeekFrom}; use capability::Cap; use core::fmt; +use crate::fops; + /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. pub type FileType = fops::FileType; /// Representation of the various permissions on a file. -pub type Permissions = fops::FilePerm; +pub type FilePerm = fops::FilePerm; -/// An object providing access to an open file on the filesystem. -pub struct File { - inner: fops::File, -} - -/// Metadata information about a file. -pub struct Metadata(fops::FileAttr); +/// A structure representing the attributes of a file. +pub type FileAttr = fops::FileAttr; /// Options and flags which can be used to configure how a file is opened. #[derive(Clone)] @@ -119,18 +115,12 @@ impl OpenOptions { } /// Opens a file at `path` with the options specified by `self`. - pub fn open(&self, path: &str) -> Result { + pub fn open(&self, path: &AbsPath) -> Result { // Check options if !self.is_valid() { return ax_err!(InvalidInput); } - // Find node - let path = if path.starts_with("/") { - AbsPath::new_canonicalized(path) - } else { - current_dir().join(&RelPath::new_canonicalized(path)) - }; - // Check flag and attr + // Find node, check flag and attr let node = match fops::lookup(&path) { Ok(node) => { if self.create_new { @@ -159,95 +149,9 @@ impl OpenOptions { } } -impl From<&OpenOptions> for Cap { - fn from(opts: &OpenOptions) -> Cap { - let mut cap = Cap::empty(); - if opts.read { - cap |= Cap::READ; - } - if opts.write | opts.append { - cap |= Cap::WRITE; - } - cap - } -} - -impl fmt::Debug for OpenOptions { - #[allow(unused_assignments)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut written = false; - macro_rules! fmt_opt { - ($field: ident, $label: literal) => { - if self.$field { - if written { - write!(f, " | ")?; - } - write!(f, $label)?; - written = true; - } - }; - } - fmt_opt!(read, "READ"); - fmt_opt!(write, "WRITE"); - fmt_opt!(append, "APPEND"); - fmt_opt!(truncate, "TRUNC"); - fmt_opt!(create, "CREATE"); - fmt_opt!(create_new, "CREATE_NEW"); - Ok(()) - } -} - -impl Metadata { - /// Returns the file type for this metadata. - pub const fn file_type(&self) -> FileType { - self.0.file_type() - } - - /// Returns `true` if this metadata is for a directory. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_file`]. - pub const fn is_dir(&self) -> bool { - self.0.is_dir() - } - - /// Returns `true` if this metadata is for a regular file. The - /// result is mutually exclusive to the result of - /// [`Metadata::is_dir`]. - pub const fn is_file(&self) -> bool { - self.0.is_file() - } - - /// Returns the size of the file, in bytes, this metadata is for. - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> u64 { - self.0.size() - } - - /// Returns the permissions of the file this metadata is for. - pub const fn permissions(&self) -> Permissions { - self.0.perm() - } - - /// Returns the total size of this file in bytes. - pub const fn size(&self) -> u64 { - self.0.size() - } - - /// Returns the number of blocks allocated to the file, in 512-byte units. - pub const fn blocks(&self) -> u64 { - self.0.blocks() - } -} - -impl fmt::Debug for Metadata { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .finish_non_exhaustive() - } +/// An object providing access to an open file on the filesystem. +pub struct File { + inner: fops::File, } impl File { @@ -285,9 +189,19 @@ impl File { self.inner.truncate(size) } - /// Queries metadata about the underlying file. - pub fn metadata(&self) -> Result { - self.inner.get_attr().map(Metadata) + /// Truncates the file to 0 length. + pub fn truncate(&self, size: u64) -> Result<()> { + self.inner.truncate(size) + } + + /// Flush the buffered contents to disk. + pub fn flush(&self) -> Result<()> { + self.inner.flush() + } + + /// Get the attributes of the file. + pub fn get_attr(&self) -> Result { + self.inner.get_attr() } } @@ -312,3 +226,41 @@ impl Seek for File { self.inner.seek(pos) } } + +impl From<&OpenOptions> for Cap { + fn from(opts: &OpenOptions) -> Cap { + let mut cap = Cap::empty(); + if opts.read { + cap |= Cap::READ; + } + if opts.write | opts.append { + cap |= Cap::WRITE; + } + cap + } +} + +impl fmt::Debug for OpenOptions { + #[allow(unused_assignments)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut written = false; + macro_rules! fmt_opt { + ($field: ident, $label: literal) => { + if self.$field { + if written { + write!(f, " | ")?; + } + write!(f, $label)?; + written = true; + } + }; + } + fmt_opt!(read, "READ"); + fmt_opt!(write, "WRITE"); + fmt_opt!(append, "APPEND"); + fmt_opt!(truncate, "TRUNC"); + fmt_opt!(create, "CREATE"); + fmt_opt!(create_new, "CREATE_NEW"); + Ok(()) + } +} diff --git a/modules/ruxfs/src/api/mod.rs b/modules/ruxfs/src/api/mod.rs index 74c59ec78..2a6ad5796 100644 --- a/modules/ruxfs/src/api/mod.rs +++ b/modules/ruxfs/src/api/mod.rs @@ -7,14 +7,13 @@ * See the Mulan PSL v2 for more details. */ -//! [`std::fs`]-like high-level filesystem manipulation operations. +//! High-level filesystem manipulation operations. +//! +//! Provided for [`arceos_api`] module and [`axstd`] user lib. mod dir; mod file; -pub use self::dir::{DirBuilder, DirEntry, ReadDir}; -pub use self::file::{File, FileType, Metadata, OpenOptions, Permissions}; - use alloc::{string::String, vec::Vec}; use axerrno::ax_err; use axfs_vfs::{AbsPath, VfsError}; @@ -22,10 +21,11 @@ use axio::{self as io, prelude::*}; use crate::fops; -/// Returns an iterator over the entries within a directory. -pub fn read_dir<'a>(path: &'a AbsPath<'a>) -> io::Result> { - ReadDir::new(path) -} +// Export high-level directory-related types. +pub use dir::{DirBuilder, DirEntry, Directory}; + +// Export high-level file-related types. +pub use file::{File, FileAttr, FilePerm, FileType, OpenOptions}; /// Returns the current working directory as a [`AbsPath`]. pub fn current_dir() -> io::Result> { @@ -37,10 +37,15 @@ pub fn set_current_dir(path: AbsPath<'static>) -> io::Result<()> { fops::set_current_dir(path) } +/// Get the attibutes of a file or directory. +pub fn get_attr(path: &AbsPath) -> io::Result { + fops::lookup(path)?.get_attr() +} + /// Read the entire contents of a file into a bytes vector. pub fn read(path: &AbsPath) -> io::Result> { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len()).unwrap_or(0); + let size = file.get_attr().map(|m| m.size()).unwrap_or(0); let mut bytes = Vec::with_capacity(size as usize); file.read_to_end(&mut bytes)?; Ok(bytes) @@ -49,7 +54,7 @@ pub fn read(path: &AbsPath) -> io::Result> { /// Read the entire contents of a file into a string. pub fn read_to_string(path: &AbsPath) -> io::Result { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len()).unwrap_or(0); + let size = file.get_attr().map(|m| m.size()).unwrap_or(0); let mut string = String::with_capacity(size as usize); file.read_to_string(&mut string)?; Ok(string) @@ -60,12 +65,6 @@ pub fn write>(path: &AbsPath, contents: C) -> io::Result<()> { File::create(path)?.write_all(contents.as_ref()) } -/// Given a path, query the file system to get information about a file, -/// directory, etc. -pub fn metadata(path: &AbsPath) -> io::Result { - File::open(path)?.metadata() -} - /// Creates a new, empty directory at the provided path. pub fn create_dir(path: &AbsPath) -> io::Result<()> { DirBuilder::new().create(path) @@ -87,7 +86,9 @@ pub fn remove_dir(path: &AbsPath) -> io::Result<()> { if !attr.perm().owner_writable() { return ax_err!(PermissionDenied); } - // TODO: check empty + if !node.is_empty()? { + return ax_err!(DirectoryNotEmpty); + } fops::remove_dir(path) } diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 566abbb1b..2b80e4a69 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -8,6 +8,11 @@ */ //! Low-level filesystem operations. +//! +//! - File: open, read, write, seek, truncate +//! - Directory: open, read, create, remove +//! +//! Provided for [ruxfs::api] and [ruxos_posix_api::fs] modules. use axerrno::{ax_err, ax_err_type, AxResult}; use axfs_vfs::{AbsPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; @@ -160,7 +165,7 @@ impl Directory { pub fn read_dir(&mut self, dirents: &mut [DirEntry]) -> AxResult { let n = self .node - .access(Cap::READ)? + .access(Cap::EXECUTE)? .read_dir(self.entry_idx, dirents)?; self.entry_idx += n; Ok(n) From 2abbac7ea1dd9b98ae05d733bab13f6fbe62047c Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 20:47:17 +0800 Subject: [PATCH 32/36] feat: fs arceos api --- api/arceos_api/src/imp/fs.rs | 56 ++++++++++++++++++++---------------- api/arceos_api/src/lib.rs | 11 ++----- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/api/arceos_api/src/imp/fs.rs b/api/arceos_api/src/imp/fs.rs index 4488e1a68..5ba4b0f13 100644 --- a/api/arceos_api/src/imp/fs.rs +++ b/api/arceos_api/src/imp/fs.rs @@ -9,14 +9,18 @@ use alloc::string::String; use axerrno::AxResult; -use ruxfs::fops::{Directory, File}; +use ruxfs::{ + api::{Directory, File}, + AbsPath, RelPath, +}; +use axio::{Read, Write, Seek}; -pub use ruxfs::fops::DirEntry as AxDirEntry; -pub use ruxfs::fops::FileAttr as AxFileAttr; -pub use ruxfs::fops::FilePerm as AxFilePerm; -pub use ruxfs::fops::FileType as AxFileType; -pub use ruxfs::fops::OpenOptions as AxOpenOptions; pub use axio::SeekFrom as AxSeekFrom; +pub use ruxfs::api::DirEntry as AxDirEntry; +pub use ruxfs::api::FileAttr as AxFileAttr; +pub use ruxfs::api::FilePerm as AxFilePerm; +pub use ruxfs::api::FileType as AxFileType; +pub use ruxfs::api::OpenOptions as AxOpenOptions; #[cfg(feature = "myfs")] pub use ruxfs::fops::{Disk as AxDisk, MyFileSystemIf}; @@ -28,29 +32,25 @@ pub struct AxFileHandle(File); pub struct AxDirHandle(Directory); pub fn ax_open_file(path: &str, opts: &AxOpenOptions) -> AxResult { - Ok(AxFileHandle(File::open(path, opts)?)) + Ok(AxFileHandle(opts.open(&parse_path(path)?)?)) } -pub fn ax_open_dir(path: &str, opts: &AxOpenOptions) -> AxResult { - Ok(AxDirHandle(Directory::open_dir(path, opts)?)) +pub fn ax_open_dir(path: &str, _opts: &AxOpenOptions) -> AxResult { + Ok(AxDirHandle(Directory::open(parse_path(path)?)?)) } -pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult { - file.0.read(buf) +pub fn ax_get_attr(path: &str) -> AxResult { + ruxfs::api::get_attr(&parse_path(path)?) } -pub fn ax_read_file_at(file: &AxFileHandle, offset: u64, buf: &mut [u8]) -> AxResult { - file.0.read_at(offset, buf) +pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult { + file.0.read(buf) } pub fn ax_write_file(file: &mut AxFileHandle, buf: &[u8]) -> AxResult { file.0.write(buf) } -pub fn ax_write_file_at(file: &AxFileHandle, offset: u64, buf: &[u8]) -> AxResult { - file.0.write_at(offset, buf) -} - pub fn ax_truncate_file(file: &AxFileHandle, size: u64) -> AxResult { file.0.truncate(size) } @@ -72,29 +72,37 @@ pub fn ax_read_dir(dir: &mut AxDirHandle, dirents: &mut [AxDirEntry]) -> AxResul } pub fn ax_create_dir(path: &str) -> AxResult { - ruxfs::api::create_dir(path) + ruxfs::api::create_dir(&parse_path(path)?) } pub fn ax_create_dir_all(path: &str) -> AxResult { - ruxfs::api::create_dir_all(path) + ruxfs::api::create_dir_all(&parse_path(path)?) } pub fn ax_remove_dir(path: &str) -> AxResult { - ruxfs::api::remove_dir(path) + ruxfs::api::remove_dir(&parse_path(path)?) } pub fn ax_remove_file(path: &str) -> AxResult { - ruxfs::api::remove_file(path) + ruxfs::api::remove_file(&parse_path(path)?) } pub fn ax_rename(old: &str, new: &str) -> AxResult { - ruxfs::api::rename(old, new) + ruxfs::api::rename(&parse_path(old)?, &parse_path(new)?) } pub fn ax_current_dir() -> AxResult { - ruxfs::api::current_dir() + ruxfs::api::current_dir().map(|path| path.to_string()) } pub fn ax_set_current_dir(path: &str) -> AxResult { - ruxfs::api::set_current_dir(path) + ruxfs::api::set_current_dir(parse_path(path)?) +} + +fn parse_path(path: &str) -> AxResult> { + if path.starts_with('/') { + Ok(AbsPath::new_canonicalized(path)) + } else { + ruxfs::api::current_dir().map(|cwd| cwd.join(&RelPath::new_canonicalized(path))) + } } diff --git a/api/arceos_api/src/lib.rs b/api/arceos_api/src/lib.rs index 0e2e0e1f6..0e4457f04 100644 --- a/api/arceos_api/src/lib.rs +++ b/api/arceos_api/src/lib.rs @@ -174,26 +174,19 @@ pub mod fs { /// Opens a directory at the path relative to the current directory with /// the options specified by `opts`. pub fn ax_open_dir(path: &str, opts: &AxOpenOptions) -> AxResult; + /// Returns attributes of a file or directory at the given path. + pub fn ax_get_attr(path: &str) -> AxResult; /// Reads the file at the current position, returns the number of bytes read. /// /// After the read, the cursor will be advanced by the number of bytes read. pub fn ax_read_file(file: &mut AxFileHandle, buf: &mut [u8]) -> AxResult; - /// Reads the file at the given position, returns the number of bytes read. - /// - /// It does not update the file cursor. - pub fn ax_read_file_at(file: &AxFileHandle, offset: u64, buf: &mut [u8]) -> AxResult; /// Writes the file at the current position, returns the number of bytes /// written. /// /// After the write, the cursor will be advanced by the number of bytes /// written. pub fn ax_write_file(file: &mut AxFileHandle, buf: &[u8]) -> AxResult; - /// Writes the file at the given position, returns the number of bytes - /// written. - /// - /// It does not update the file cursor. - pub fn ax_write_file_at(file: &AxFileHandle, offset: u64, buf: &[u8]) -> AxResult; /// Truncates the file to the specified size. pub fn ax_truncate_file(file: &AxFileHandle, size: u64) -> AxResult; /// Flushes the file, writes all buffered data to the underlying device. From b4c419bb230b511e041d5d0722b88106a46639f8 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 20:47:48 +0800 Subject: [PATCH 33/36] feat: axstd fs api --- apps/fs/shell/src/cmd.rs | 11 +++++++---- ulib/axstd/src/fs/dir.rs | 19 +++++++++++++------ ulib/axstd/src/fs/file.rs | 7 ++++++- ulib/axstd/src/fs/mod.rs | 2 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/fs/shell/src/cmd.rs b/apps/fs/shell/src/cmd.rs index c240231dd..fb5d61d1c 100644 --- a/apps/fs/shell/src/cmd.rs +++ b/apps/fs/shell/src/cmd.rs @@ -104,10 +104,13 @@ fn do_ls(args: &str) { if print_name { println!("{}:", name); } - let mut entries = fs::read_dir(name)? - .filter_map(|e| e.ok()) - .map(|e| e.file_name()) - .collect::>(); + let mut entries = Vec::new(); + for entry in fs::read_dir(name)? { + match entry { + Ok(entry) => entries.push(entry.file_name()), + Err(e) => return Err(e), + } + } entries.sort(); for entry in entries { diff --git a/ulib/axstd/src/fs/dir.rs b/ulib/axstd/src/fs/dir.rs index 2f60a9a12..274c7e7d9 100644 --- a/ulib/axstd/src/fs/dir.rs +++ b/ulib/axstd/src/fs/dir.rs @@ -11,6 +11,7 @@ extern crate alloc; use alloc::string::String; use core::fmt; +use core::mem::MaybeUninit; use super::FileType; use crate::io::Result; @@ -46,8 +47,15 @@ impl<'a> ReadDir<'a> { opts.read(true); let inner = api::ax_open_dir(path, &opts)?; - const EMPTY: api::AxDirEntry = api::AxDirEntry::default(); - let dirent_buf = [EMPTY; 31]; + let dirent_buf: [api::AxDirEntry; 31] = unsafe { + let mut buf: MaybeUninit<[api::AxDirEntry; 31]> = MaybeUninit::uninit(); + let buf_ptr = buf.as_mut_ptr() as *mut api::AxDirEntry; + for i in 0..31 { + buf_ptr.add(i).write(api::AxDirEntry::default()); + } + buf.assume_init() + }; + Ok(ReadDir { path, inner, @@ -86,12 +94,11 @@ impl<'a> Iterator for ReadDir<'a> { } let entry = &self.dirent_buf[self.buf_pos]; self.buf_pos += 1; - let name_bytes = entry.name_as_bytes(); - if name_bytes == b"." || name_bytes == b".." { + let entry_name = entry.file_name(); + if entry_name == "." || entry_name == ".." { continue; } - let entry_name = unsafe { core::str::from_utf8_unchecked(name_bytes).into() }; - let entry_type = entry.entry_type(); + let entry_type = entry.file_type(); return Some(Ok(DirEntry { dir_path: self.path, diff --git a/ulib/axstd/src/fs/file.rs b/ulib/axstd/src/fs/file.rs index e89a5a604..e6778dc78 100644 --- a/ulib/axstd/src/fs/file.rs +++ b/ulib/axstd/src/fs/file.rs @@ -80,6 +80,11 @@ impl OpenOptions { } impl Metadata { + /// Wraps an `AxFileAttr` in a `Metadata` object. + pub(super) const fn new(attr: api::AxFileAttr) -> Self { + Metadata(attr) + } + /// Returns the file type for this metadata. pub const fn file_type(&self) -> FileType { self.0.file_type() @@ -185,7 +190,7 @@ impl Write for File { } fn flush(&mut self) -> Result<()> { - api::ax_flush_file(&self.inner) + api::ax_flush_file(&mut self.inner) } } diff --git a/ulib/axstd/src/fs/mod.rs b/ulib/axstd/src/fs/mod.rs index 85815aa28..fa60996e1 100644 --- a/ulib/axstd/src/fs/mod.rs +++ b/ulib/axstd/src/fs/mod.rs @@ -48,7 +48,7 @@ pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result< /// Given a path, query the file system to get information about a file, /// directory, etc. pub fn metadata>(path: P) -> io::Result { - File::open(path.as_ref())?.metadata() + arceos_api::fs::ax_get_attr(path.as_ref()).map(|attr| Metadata::new(attr)) } /// Returns an iterator over the entries within a directory. From 0b87e7c355f4bc6a66c0eba09f6bb177d58f3217 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 21:10:10 +0800 Subject: [PATCH 34/36] fix: devfs and ramfs lookup --- crates/axfs_devfs/src/dir.rs | 33 +++++++++++++++++++-------------- crates/axfs_ramfs/src/dir.rs | 34 ++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/crates/axfs_devfs/src/dir.rs b/crates/axfs_devfs/src/dir.rs index afc490d71..f98b9e8d5 100644 --- a/crates/axfs_devfs/src/dir.rs +++ b/crates/axfs_devfs/src/dir.rs @@ -73,19 +73,27 @@ impl VfsNodeOps for DirNode { fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); - let node = match name { - "" => Ok(self.clone() as VfsNodeRef), - _ => self - .children + if let Some(rest) = rest { + match name { + ".." => self.parent().ok_or(VfsError::NotFound)?.lookup(&rest), + _ => self + .children + .read() + .get(name) + .cloned() + .ok_or(VfsError::NotFound)? + .lookup(&rest), + } + } else if name == "" { + Ok(self.clone() as VfsNodeRef) + } else if name == ".." { + self.parent().ok_or(VfsError::NotFound) + } else { + self.children .read() .get(name) .cloned() - .ok_or(VfsError::NotFound), - }?; - if let Some(rest) = rest { - node.lookup(&rest) - } else { - Ok(node) + .ok_or(VfsError::NotFound) } } @@ -109,11 +117,9 @@ impl VfsNodeOps for DirNode { } fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at devfs: {}", ty, path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { - "" | "." => self.create(&rest, ty), ".." => self.parent().ok_or(VfsError::NotFound)?.create(&rest, ty), _ => self .children @@ -122,7 +128,7 @@ impl VfsNodeOps for DirNode { .ok_or(VfsError::NotFound)? .create(&rest, ty), } - } else if name.is_empty() || name == "." || name == ".." { + } else if name == "" || name == ".." { Ok(()) // already exists } else { Err(VfsError::PermissionDenied) // do not support to create nodes dynamically @@ -130,7 +136,6 @@ impl VfsNodeOps for DirNode { } fn unlink(&self, path: &RelPath) -> VfsResult { - log::debug!("unlink at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { diff --git a/crates/axfs_ramfs/src/dir.rs b/crates/axfs_ramfs/src/dir.rs index 6dafd329f..d85b35bc7 100644 --- a/crates/axfs_ramfs/src/dir.rs +++ b/crates/axfs_ramfs/src/dir.rs @@ -102,19 +102,27 @@ impl VfsNodeOps for DirNode { fn lookup(self: Arc, path: &RelPath) -> VfsResult { let (name, rest) = split_path(path); - let node = match name { - "" => Ok(self.clone() as VfsNodeRef), - _ => self - .children + if let Some(rest) = rest { + match name { + ".." => self.parent().ok_or(VfsError::NotFound)?.lookup(&rest), + _ => self + .children + .read() + .get(name) + .cloned() + .ok_or(VfsError::NotFound)? + .lookup(&rest), + } + } else if name == "" { + Ok(self.clone() as VfsNodeRef) + } else if name == ".." { + self.parent().ok_or(VfsError::NotFound) + } else { + self.children .read() .get(name) .cloned() - .ok_or(VfsError::NotFound), - }?; - if let Some(rest) = rest { - node.lookup(&rest) - } else { - Ok(node) + .ok_or(VfsError::NotFound) } } @@ -138,7 +146,6 @@ impl VfsNodeOps for DirNode { } fn create(&self, path: &RelPath, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at devfs: {}", ty, path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { @@ -150,7 +157,7 @@ impl VfsNodeOps for DirNode { .ok_or(VfsError::NotFound)? .create(&rest, ty), } - } else if name.is_empty() || name == ".." { + } else if name == "" || name == ".." { Ok(()) // already exists } else { self.create_node(name, ty) @@ -158,7 +165,6 @@ impl VfsNodeOps for DirNode { } fn unlink(&self, path: &RelPath) -> VfsResult { - log::debug!("remove at devfs: {}", path); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { @@ -170,7 +176,7 @@ impl VfsNodeOps for DirNode { .ok_or(VfsError::NotFound)? .unlink(&rest), } - } else if name.is_empty() || name == ".." { + } else if name == "" || name == ".." { Err(VfsError::InvalidInput) // remove '.' or '.. } else { self.remove_node(name) From b0fa8f75bf5b4b3bd6319533cd8f6f9e12f2ef22 Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 21:27:00 +0800 Subject: [PATCH 35/36] doc: axfs_vfs and ruxfs doc comments --- crates/axfs_vfs/src/lib.rs | 5 ++++- crates/axfs_vfs/src/path.rs | 14 ++++++------ modules/ruxfs/src/fops.rs | 21 +++++++++--------- modules/ruxfs/src/lib.rs | 4 ++-- modules/ruxfs/src/root.rs | 43 ++++++++++++++++++++++--------------- 5 files changed, 49 insertions(+), 38 deletions(-) diff --git a/crates/axfs_vfs/src/lib.rs b/crates/axfs_vfs/src/lib.rs index ac8fe5b17..5344e33fa 100644 --- a/crates/axfs_vfs/src/lib.rs +++ b/crates/axfs_vfs/src/lib.rs @@ -31,6 +31,7 @@ //! | [`open()`](VfsNodeOps::open) | Do something when the node is opened | both | //! | [`release()`](VfsNodeOps::release) | Do something when the node is closed | both | //! | [`get_attr()`](VfsNodeOps::get_attr) | Get the attributes of the node | both | +//! | [`set_attr()`](VfsNodeOps::set_attr) | Set the attributes of the node | both | //! | [`read_at()`](VfsNodeOps::read_at) | Read data from the file | file | //! | [`write_at()`](VfsNodeOps::write_at) | Write data to the file | file | //! | [`fsync()`](VfsNodeOps::fsync) | Synchronize the file data to disk | file | @@ -38,8 +39,10 @@ //! | [`parent()`](VfsNodeOps::parent) | Get the parent directory | directory | //! | [`lookup()`](VfsNodeOps::lookup) | Lookup the node with the given path | directory | //! | [`create()`](VfsNodeOps::create) | Create a new node with the given path | directory | -//! | [`remove()`](VfsNodeOps::remove) | Remove the node with the given path | directory | +//! | [`link()`](VfsNodeOps::link) | Create a hard link with the given path | directory | +//! | [`unlink()`](VfsNodeOps::unlink) | Remove the node with the given path | directory | //! | [`read_dir()`](VfsNodeOps::read_dir) | Read directory entries | directory | +//! | [`is_empty()`](VfsNodeOps::is_empty) | [unstable] Check if the directory is empty | directory | //! //! [inodes]: https://en.wikipedia.org/wiki/Inode diff --git a/crates/axfs_vfs/src/path.rs b/crates/axfs_vfs/src/path.rs index a18daefa8..bd4b1d099 100644 --- a/crates/axfs_vfs/src/path.rs +++ b/crates/axfs_vfs/src/path.rs @@ -15,12 +15,12 @@ use alloc::{ string::{String, ToString}, }; -/// Canonicalized absolute path type. +/// Canonicalized absolute path type. Requirements: /// /// - Starting with `/` /// - No `.` or `..` components /// - No redundant or tailing `/` -/// - Valid examples: "/", "/root/foo/bar" +/// - Valid examples: `/`, `/root/foo/bar` /// /// Using `Cow` type to avoid unnecessary allocations. #[derive(Debug, Clone, PartialEq, Eq)] @@ -95,13 +95,13 @@ impl core::fmt::Display for AbsPath<'_> { } } -/// Canonicalized relative path type. +/// Canonicalized relative path type. Requirements: /// -/// - No starting '/' +/// - No starting `/` /// - No `.` components -/// - No redundant or tailing '/' -/// - Possibly starts with '..' -/// - Valid examples: "", "..", "../b", "../.." +/// - No redundant or tailing `/` +/// - Possibly starts with `..` +/// - Valid examples: ` `, `..`, `../b`, `../..` /// /// Using `Cow` type to avoid unnecessary allocations. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/modules/ruxfs/src/fops.rs b/modules/ruxfs/src/fops.rs index 2b80e4a69..34e9b3927 100644 --- a/modules/ruxfs/src/fops.rs +++ b/modules/ruxfs/src/fops.rs @@ -7,12 +7,12 @@ * See the Mulan PSL v2 for more details. */ -//! Low-level filesystem operations. -//! +//! Low-level filesystem operations. Provided for [ruxfs::api] and [ruxos_posix_api::fs] modules. +//! //! - File: open, read, write, seek, truncate //! - Directory: open, read, create, remove -//! -//! Provided for [ruxfs::api] and [ruxos_posix_api::fs] modules. +//! +//! The interface is designed with low coupling to avoid repetitive error handling. use axerrno::{ax_err, ax_err_type, AxResult}; use axfs_vfs::{AbsPath, VfsError, VfsNodeOps, VfsNodeRef, VfsNodeType}; @@ -39,18 +39,18 @@ pub type FilePerm = axfs_vfs::VfsNodePerm; pub struct File { path: AbsPath<'static>, node: WithCap, - is_append: bool, + append: bool, offset: u64, } impl File { /// Create an opened file. - pub fn new(path: AbsPath<'static>, node: VfsNodeRef, cap: Cap, is_append: bool) -> Self { + pub fn new(path: AbsPath<'static>, node: VfsNodeRef, cap: Cap, append: bool) -> Self { Self { path, node: WithCap::new(node, cap), offset: 0, - is_append, + append, } } @@ -93,7 +93,7 @@ impl File { /// written. pub fn write(&mut self, buf: &[u8]) -> AxResult { let node = self.node.access(Cap::WRITE)?; - if self.is_append { + if self.append { self.offset = self.get_attr()?.size(); }; let write_len = node.write_at(self.offset, buf)?; @@ -129,8 +129,7 @@ impl File { } } -/// An opened directory object, with open permissions and a cursor for -/// [`read_dir`](Directory::read_dir). +/// An opened directory object, with open permissions and a cursor for entry reading. pub struct Directory { path: AbsPath<'static>, node: WithCap, @@ -182,7 +181,7 @@ impl Directory { } } -/* File operations with absolute path */ +// File operations with absolute path. /// Look up a file given an absolute path. pub fn lookup(path: &AbsPath) -> AxResult { diff --git a/modules/ruxfs/src/lib.rs b/modules/ruxfs/src/lib.rs index 2ecbbb561..38b20f73b 100644 --- a/modules/ruxfs/src/lib.rs +++ b/modules/ruxfs/src/lib.rs @@ -13,8 +13,8 @@ //! //! # Cargo Features //! -//! - `fatfs`: Use [FAT] as the main filesystem and mount it on `/`. This feature -//! is **enabled** by default. +//! - `fatfs`: Use [FAT] as the main filesystem and mount it on `/`. Requires +//! `blkfs` to be enabled. //! - `devfs`: Mount [`axfs_devfs::DeviceFileSystem`] on `/dev`. This feature is //! **enabled** by default. //! - `ramfs`: Mount [`axfs_ramfs::RamFileSystem`] on `/tmp`. This feature is diff --git a/modules/ruxfs/src/root.rs b/modules/ruxfs/src/root.rs index fa0a713ae..6f4e1b409 100644 --- a/modules/ruxfs/src/root.rs +++ b/modules/ruxfs/src/root.rs @@ -7,9 +7,10 @@ * See the Mulan PSL v2 for more details. */ -//! Root directory of the filesystem +//! Root directory of the filesystem. Filesystem operations are distributed to the +//! appropriate filesystem based on the mount points. //! -//! TODO: it doesn't work very well if the mount points have containment relationships. +//! `RootDirectory::lookup_mounted_fs()` performs the distribution of operations. use alloc::{format, sync::Arc, vec::Vec}; use axerrno::{ax_err, AxResult}; @@ -19,24 +20,12 @@ use axfs_vfs::{ use axsync::Mutex; use lazy_init::LazyInit; -use crate::api::FileType; - -pub(crate) static CURRENT_DIR: Mutex = Mutex::new(AbsPath::new("/")); - /// mount point information pub struct MountPoint { path: AbsPath<'static>, fs: Arc, } -/// Root directory of the main filesystem -pub(crate) struct RootDirectory { - main_fs: Arc, - mounts: Vec, -} - -pub(crate) static ROOT_DIR: LazyInit> = LazyInit::new(); - impl MountPoint { /// create new MountPoint from data pub fn new(path: AbsPath<'static>, fs: Arc) -> Self { @@ -50,6 +39,12 @@ impl Drop for MountPoint { } } +/// Root directory of the main filesystem +pub(crate) struct RootDirectory { + main_fs: Arc, + mounts: Vec, +} + impl RootDirectory { pub const fn new(main_fs: Arc) -> Self { Self { @@ -67,12 +62,20 @@ impl RootDirectory { } // create the mount point in the main filesystem if it does not exist match self.main_fs.root_dir().lookup(&path.to_rel()) { - Ok(_) => {} + Ok(node) => { + if !node.get_attr()?.is_dir() { + return ax_err!(InvalidInput, "mount point is not a directory"); + } + if !node.is_empty()? { + return ax_err!(InvalidInput, "mount point is not empty"); + } + // TODO: permission check + } Err(e) => { if e == VfsError::NotFound { self.main_fs .root_dir() - .create(&path.to_rel(), FileType::Dir)?; + .create(&path.to_rel(), VfsNodeType::Dir)?; } else { return Err(e); } @@ -99,7 +102,6 @@ impl RootDirectory { let mut max_len = 0; // Find the filesystem that has the longest mounted path match - // TODO: more efficient, e.g. trie for (i, mp) in self.mounts.iter().enumerate() { let rel_mp = mp.path.to_rel(); // path must have format: "" or "/..." @@ -177,6 +179,13 @@ impl VfsNodeOps for RootDirectory { } } +/// Current working directory. +pub(crate) static CURRENT_DIR: Mutex = Mutex::new(AbsPath::new("/")); + +/// Root directory of the virtual filesystem. +pub(crate) static ROOT_DIR: LazyInit> = LazyInit::new(); + +/// Initialize virtual filesystem. pub(crate) fn init_rootfs(mount_points: Vec) { let main_fs = mount_points .first() From 9251a66c43503824c58ee0cb734e1d1632fd66ab Mon Sep 17 00:00:00 2001 From: liujingx Date: Thu, 28 Nov 2024 22:10:24 +0800 Subject: [PATCH 36/36] fix: remove harness from workspace --- Cargo.lock | 76 ++---------------------------------------------------- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e3756d7d..d90de464c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,12 +468,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - [[package]] name = "const-default" version = "1.0.0" @@ -876,17 +870,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e81913856cda07e1e7044a375043c2e4429ddb7d94a0d4ad10d4c27796ce4bd9" -[[package]] -name = "harness" -version = "0.1.0" -dependencies = [ - "axstd", - "km-command", - "km-harness", - "log", - "ruxos_posix_api", -] - [[package]] name = "hash32" version = "0.2.1" @@ -896,15 +879,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -918,23 +892,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", - "hash32 0.2.1", + "hash32", "rustc_version 0.4.1", - "serde", "spin", "stable_deref_trait", ] -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32 0.3.1", - "stable_deref_trait", -] - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1058,25 +1021,6 @@ dependencies = [ "crate_interface", ] -[[package]] -name = "km-command" -version = "0.1.0" -dependencies = [ - "bitflags 2.6.0", - "heapless 0.8.0", - "postcard", - "serde", -] - -[[package]] -name = "km-harness" -version = "0.1.0" -source = "git+https://github.com/LJxTHUCS/km-harness.git#bc94ebe057ba482828baa8893e1b9cbe02677d3a" -dependencies = [ - "libafl_qemu_cmd", - "serde", -] - [[package]] name = "lazy_init" version = "0.1.0" @@ -1096,11 +1040,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "libafl_qemu_cmd" -version = "0.1.0" -source = "git+https://github.com/nine-point-eight-p/libafl_qemu_cmd#9873bf57108d4535d8bf5f7c458039b88a649367" - [[package]] name = "libc" version = "0.2.158" @@ -1356,17 +1295,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "postcard" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" -dependencies = [ - "cobs", - "heapless 0.7.17", - "serde", -] - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -2030,7 +1958,7 @@ dependencies = [ "byteorder", "cfg-if", "defmt", - "heapless 0.7.17", + "heapless", "log", "managed", ] diff --git a/Cargo.toml b/Cargo.toml index df0512a6f..d4f1f9d80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ members = [ "apps/display/basic_painting", "apps/display/draw_map", - "apps/fs/shell", "apps/custom/harness", + "apps/fs/shell" ] [profile.release]