diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 49b68222..8e20210b 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -2,11 +2,11 @@ use alloc::string::ToString; use core::cmp::Ordering; use core::intrinsics::unlikely; use core::{any::Any, fmt::Debug}; +use hashbrown::HashMap; use log::error; use system_error::SystemError; use alloc::{ - collections::BTreeMap, string::String, sync::{Arc, Weak}, vec::Vec, @@ -36,6 +36,7 @@ use crate::{ }; use super::entry::FATFile; +use super::utils::{to_search_name, to_search_name_string}; use super::{ bpb::{BiosParameterBlock, FATType}, entry::{FATDir, FATDirEntry, FATDirIter, FATEntry}, @@ -106,9 +107,9 @@ pub struct FATInode { parent: Weak, /// 指向自身的弱引用 self_ref: Weak, - /// 子Inode的B树. 该数据结构用作缓存区。其中,它的key表示inode的名称。 + /// 子Inode的map. 该数据结构用作缓存区。其中,它的key表示inode的名称。 /// 请注意,由于FAT的查询过程对大小写不敏感,因此我们选择让key全部是大写的,方便统一操作。 - children: BTreeMap>, + children: HashMap>, /// 当前inode的元数据 metadata: Metadata, /// 指向inode所在的文件系统对象的指针 @@ -148,24 +149,25 @@ impl FATInode { fn find(&mut self, name: &str) -> Result, SystemError> { match &self.inode_type { FATDirEntry::Dir(d) => { - let dname = DName::from(name.to_uppercase()); + let search_name = to_search_name(name); // 尝试在缓存区查找 - if let Some(entry) = self.children.get(&dname) { + if let Some(entry) = self.children.get(&search_name) { return Ok(entry.clone()); } // 在缓存区找不到 // 在磁盘查找 let fat_entry: FATDirEntry = d.find_entry(name, None, None, self.fs.upgrade().unwrap())?; + let dname = DName::from(name); // 创建新的inode let entry_inode: Arc = LockedFATInode::new( - dname.clone(), + dname, self.fs.upgrade().unwrap(), self.self_ref.clone(), fat_entry, ); // 加入缓存区, 由于FAT文件系统的大小写不敏感问题,因此存入缓存区的key应当是全大写的 - self.children.insert(dname, entry_inode.clone()); + self.children.insert(search_name, entry_inode.clone()); return Ok(entry_inode); } FATDirEntry::UnInit => { @@ -197,7 +199,7 @@ impl LockedFATInode { let inode: Arc = Arc::new(LockedFATInode(SpinLock::new(FATInode { parent, self_ref: Weak::default(), - children: BTreeMap::new(), + children: HashMap::new(), fs: Arc::downgrade(&fs), inode_type, metadata: Metadata { @@ -348,7 +350,7 @@ impl FATFileSystem { let root_inode: Arc = Arc::new(LockedFATInode(SpinLock::new(FATInode { parent: Weak::default(), self_ref: Weak::default(), - children: BTreeMap::new(), + children: HashMap::new(), fs: Weak::default(), inode_type: FATDirEntry::UnInit, metadata: Metadata { @@ -1559,14 +1561,15 @@ impl IndexNode for LockedFATInode { for ent in dir_iter { ret.push(ent.name()); - // ====== 生成inode缓存,存入B树 - let name = DName::from(ent.name().to_uppercase()); + // ====== 生成inode缓存 + let search_name = to_search_name_string(ent.name()); // debug!("name={name}"); - if !guard.children.contains_key(&name) - && name.as_ref() != "." - && name.as_ref() != ".." + if !guard.children.contains_key(&search_name) + && search_name != "." + && search_name != ".." { + let name = DName::from(ent.name()); // 创建新的inode let entry_inode: Arc = LockedFATInode::new( name.clone(), @@ -1575,7 +1578,7 @@ impl IndexNode for LockedFATInode { ent, ); // 加入缓存区, 由于FAT文件系统的大小写不敏感问题,因此存入缓存区的key应当是全大写的 - guard.children.insert(name, entry_inode.clone()); + guard.children.insert(search_name, entry_inode.clone()); } } return Ok(ret); @@ -1611,7 +1614,7 @@ impl IndexNode for LockedFATInode { // 对目标inode上锁,以防更改 let target_guard: SpinLockGuard = target.0.lock(); // 先从缓存删除 - let nod = guard.children.remove(&DName::from(name.to_uppercase())); + let nod = guard.children.remove(&to_search_name(name)); // 若删除缓存中为管道的文件,则不需要再到磁盘删除 if nod.is_some() { @@ -1646,7 +1649,7 @@ impl IndexNode for LockedFATInode { // 对目标inode上锁,以防更改 let target_guard: SpinLockGuard = target.0.lock(); // 先从缓存删除 - guard.children.remove(&DName::from(name.to_uppercase())); + guard.children.remove(&to_search_name(name)); let dir = match &guard.inode_type { FATDirEntry::File(_) | FATDirEntry::VolId(_) => { @@ -1669,9 +1672,7 @@ impl IndexNode for LockedFATInode { Err(r) => { if r == SystemError::ENOTEMPTY { // 如果要删除的是目录,且不为空,则删除动作未发生,重新加入缓存 - guard - .children - .insert(DName::from(name.to_uppercase()), target.clone()); + guard.children.insert(to_search_name(name), target.clone()); drop(target_guard); } return Err(r); @@ -1695,7 +1696,6 @@ impl IndexNode for LockedFATInode { let old_inode_guard: SpinLockGuard = old_inode.0.lock(); let fs = old_inode_guard.fs.upgrade().unwrap(); // 从缓存删除 - let _nod = guard.children.remove(&DName::from(old_name.to_uppercase())); let old_dir = match &guard.inode_type { FATDirEntry::File(_) | FATDirEntry::VolId(_) => { return Err(SystemError::ENOTDIR); @@ -1710,6 +1710,7 @@ impl IndexNode for LockedFATInode { // old_dir.check_existence(old_name, Some(false), guard.fs.upgrade().unwrap())?; old_dir.rename(fs, old_name, new_name)?; + let _nod = guard.children.remove(&to_search_name(old_name)); } else { let mut old_guard = self.0.lock(); let other: &LockedFATInode = target @@ -1721,10 +1722,7 @@ impl IndexNode for LockedFATInode { // 对目标inode上锁,以防更改 let old_inode_guard: SpinLockGuard = old_inode.0.lock(); let fs = old_inode_guard.fs.upgrade().unwrap(); - // 从缓存删除 - let _nod = old_guard - .children - .remove(&DName::from(old_name.to_uppercase())); + let old_dir = match &old_guard.inode_type { FATDirEntry::File(_) | FATDirEntry::VolId(_) => { return Err(SystemError::ENOTDIR); @@ -1748,6 +1746,8 @@ impl IndexNode for LockedFATInode { // 检查文件是否存在 old_dir.check_existence(old_name, Some(false), old_guard.fs.upgrade().unwrap())?; old_dir.rename_across(fs, new_dir, old_name, new_name)?; + // 从缓存删除 + let _nod = old_guard.children.remove(&to_search_name(old_name)); } return Ok(()); @@ -1806,9 +1806,9 @@ impl IndexNode for LockedFATInode { return self.create(filename, FileType::File, mode); } - let filename = DName::from(filename.to_uppercase()); + let dname = DName::from(filename); let nod = LockedFATInode::new( - filename.clone(), + dname, inode.fs.upgrade().unwrap(), inode.self_ref.clone(), FATDirEntry::File(FATFile::default()), @@ -1830,7 +1830,7 @@ impl IndexNode for LockedFATInode { return Err(SystemError::EINVAL); } - inode.children.insert(filename, nod.clone()); + inode.children.insert(to_search_name(filename), nod.clone()); Ok(nod) } diff --git a/kernel/src/filesystem/fat/utils.rs b/kernel/src/filesystem/fat/utils.rs index 97f5a823..329cb84a 100644 --- a/kernel/src/filesystem/fat/utils.rs +++ b/kernel/src/filesystem/fat/utils.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use core::char::REPLACEMENT_CHARACTER; /// FAT文件系统保留开头的2个簇 @@ -5,7 +6,7 @@ pub const RESERVED_CLUSTERS: u32 = 2; /// @brief 将u8转为ascii字符。 /// 当转码成功时,返回对应的ascii字符,否则返回Unicode占位符 -pub fn decode_u8_ascii(value: u8) -> char { +pub(super) fn decode_u8_ascii(value: u8) -> char { if value <= 0x7f { return value as char; } else { @@ -13,3 +14,16 @@ pub fn decode_u8_ascii(value: u8) -> char { return REPLACEMENT_CHARACTER; } } + +/// 把名称转为inode缓存里面的key +#[inline(always)] +pub(super) fn to_search_name(name: &str) -> String { + name.to_ascii_uppercase() +} + +/// 把名称转为inode缓存里面的key(输入为string,原地替换) +#[inline(always)] +pub(super) fn to_search_name_string(mut name: String) -> String { + name.make_ascii_uppercase(); + name +} diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index e519417b..85ea2c4f 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -12,9 +12,13 @@ use crate::{ procfs::procfs_init, ramfs::RamFS, sysfs::sysfs_init, - vfs::{mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType}, + vfs::{ + mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType, MAX_PATHLEN, + }, }, + libs::spinlock::SpinLock, process::ProcessManager, + syscall::user_access::check_and_clone_cstr, }; use super::{ @@ -23,7 +27,7 @@ use super::{ mount::{init_mountlist, MOUNT_LIST}, syscall::UmountFlag, utils::{rsplit_path, user_path_at}, - IndexNode, InodeId, VFS_MAX_FOLLOW_SYMLINK_TIMES, + FilePrivateData, IndexNode, InodeId, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; /// 当没有指定根文件系统时,尝试的根文件系统列表 @@ -248,6 +252,48 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result { return Ok(0); } +pub fn do_symlinkat(from: *const u8, newdfd: i32, to: *const u8) -> Result { + let oldname = check_and_clone_cstr(from, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let newname = check_and_clone_cstr(to, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let from = oldname.as_str().trim(); + let to = newname.as_str().trim(); + + // TODO: 添加权限检查,确保进程拥有目标路径的权限 + + let pcb = ProcessManager::current_pcb(); + let (old_begin_inode, old_remain_path) = user_path_at(&pcb, AtFlags::AT_FDCWD.bits(), from)?; + // info!("old_begin_inode={:?}", old_begin_inode.metadata()); + let _ = + old_begin_inode.lookup_follow_symlink(&old_remain_path, VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + + // 得到新创建节点的父节点 + let (new_begin_inode, new_remain_path) = user_path_at(&pcb, newdfd, to)?; + let (new_name, new_parent_path) = rsplit_path(&new_remain_path); + let new_parent = new_begin_inode + .lookup_follow_symlink(new_parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + // info!("new_parent={:?}", new_parent.metadata()); + + if new_parent.metadata()?.file_type != FileType::Dir { + return Err(SystemError::ENOTDIR); + } + + let new_inode = new_parent.create_with_data( + new_name, + FileType::SymLink, + ModeType::from_bits_truncate(0o777), + 0, + )?; + + let buf = old_remain_path.as_bytes(); + let len = buf.len(); + new_inode.write_at(0, len, buf, SpinLock::new(FilePrivateData::Unused).lock())?; + return Ok(0); +} + /// # do_mount - 挂载文件系统 /// /// 将给定的文件系统挂载到指定的挂载点。 diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index c4ab9079..12f122b8 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -8,6 +8,7 @@ use alloc::{ collections::BTreeMap, string::{String, ToString}, sync::{Arc, Weak}, + vec::Vec, }; use system_error::SystemError; @@ -215,12 +216,29 @@ impl MountFSInode { .ok_or(SystemError::ENOENT); } - fn do_absolute_path(&self, len: usize) -> Result { - if self.metadata()?.inode_id == ROOT_INODE().metadata()?.inode_id { - return Ok(String::with_capacity(len)); + fn do_absolute_path(&self) -> Result { + let mut path_parts = Vec::new(); + let mut current = self.self_ref.upgrade().unwrap(); + + while current.metadata()?.inode_id != ROOT_INODE().metadata()?.inode_id { + let name = current.dname()?; + path_parts.push(name.0); + current = current.do_parent()?; } - let name = self.dname()?; - return Ok(self.do_parent()?.do_absolute_path(len + name.0.len() + 1)? + "/" + &name.0); + + // 由于我们从叶子节点向上遍历到根节点,所以需要反转路径部分 + path_parts.reverse(); + + // 构建最终的绝对路径字符串 + let mut absolute_path = String::with_capacity( + path_parts.iter().map(|s| s.len()).sum::() + path_parts.len(), + ); + for part in path_parts { + absolute_path.push('/'); + absolute_path.push_str(&part); + } + + Ok(absolute_path) } } @@ -469,7 +487,7 @@ impl IndexNode for MountFSInode { } fn absolute_path(&self) -> Result { - self.do_absolute_path(0) + self.do_absolute_path() } #[inline] diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 545d05f0..5d256bf0 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -20,6 +20,7 @@ use crate::{ time::{syscall::PosixTimeval, PosixTimeSpec}, }; +use super::core::do_symlinkat; use super::{ core::{do_mkdir_at, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, @@ -980,6 +981,18 @@ impl Syscall { return do_unlink_at(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize); } + pub fn symlink(oldname: *const u8, newname: *const u8) -> Result { + return do_symlinkat(oldname, AtFlags::AT_FDCWD.bits(), newname); + } + + pub fn symlinkat( + oldname: *const u8, + newdfd: i32, + newname: *const u8, + ) -> Result { + return do_symlinkat(oldname, newdfd, newname); + } + /// # 修改文件名 /// /// diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 3502f6ff..c61c2db5 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -346,6 +346,20 @@ impl Syscall { Self::unlinkat(dirfd, path, flags) } + #[cfg(target_arch = "x86_64")] + SYS_SYMLINK => { + let oldname = args[0] as *const u8; + let newname = args[1] as *const u8; + Self::symlink(oldname, newname) + } + + SYS_SYMLINKAT => { + let oldname = args[0] as *const u8; + let newdfd = args[1] as i32; + let newname = args[2] as *const u8; + Self::symlinkat(oldname, newdfd, newname) + } + #[cfg(target_arch = "x86_64")] SYS_RMDIR => { let path = args[0] as *const u8; diff --git a/user/apps/test-mount/Cargo.toml b/user/apps/test-mount/Cargo.toml index a379b060..912080a3 100644 --- a/user/apps/test-mount/Cargo.toml +++ b/user/apps/test-mount/Cargo.toml @@ -8,4 +8,5 @@ authors = [ "xiaolin2004 <1553367438@qq.com>" ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libc="0.2" \ No newline at end of file +errno = "0.3.9" +libc="0.2" diff --git a/user/apps/test-mount/src/main.rs b/user/apps/test-mount/src/main.rs index 271310d0..10279cb6 100644 --- a/user/apps/test-mount/src/main.rs +++ b/user/apps/test-mount/src/main.rs @@ -1,8 +1,10 @@ use core::ffi::{c_char, c_void}; +use errno::errno; use libc::{mount, MS_BIND}; use std::fs; use std::path::Path; use std::time; + fn main() { let path = Path::new("mnt/tmp"); let dir = fs::create_dir_all(path); @@ -26,7 +28,8 @@ fn main() { if result == 0 { println!("Mount successful"); } else { - println!("Mount failed"); + let err = errno(); + println!("Mount failed with error code: {}", err.0); } let dur = clock.elapsed(); println!("mount costing time: {} ns", dur.as_nanos()); diff --git a/user/apps/test-symlink/.gitignore b/user/apps/test-symlink/.gitignore new file mode 100644 index 00000000..7ca01a55 --- /dev/null +++ b/user/apps/test-symlink/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ diff --git a/user/apps/test-symlink/Cargo.toml b/user/apps/test-symlink/Cargo.toml new file mode 100644 index 00000000..bbad89e7 --- /dev/null +++ b/user/apps/test-symlink/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test-symlink" +version = "0.1.0" +edition = "2021" +description = "测试symlink系统调用" +authors = [ "sparkzky " ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +errno = "0.3.9" +libc="0.2" +nix = "0.23" \ No newline at end of file diff --git a/user/apps/test-symlink/Makefile b/user/apps/test-symlink/Makefile new file mode 100644 index 00000000..7522ea16 --- /dev/null +++ b/user/apps/test-symlink/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN= +RUSTFLAGS= + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test-symlink/README.md b/user/apps/test-symlink/README.md new file mode 100644 index 00000000..6fe2bdc6 --- /dev/null +++ b/user/apps/test-symlink/README.md @@ -0,0 +1 @@ +# 测试Symlink系统调用的程序 diff --git a/user/apps/test-symlink/src/main.rs b/user/apps/test-symlink/src/main.rs new file mode 100644 index 00000000..af070694 --- /dev/null +++ b/user/apps/test-symlink/src/main.rs @@ -0,0 +1,78 @@ +extern crate libc; +use core::ffi::{c_char, c_void}; +use libc::{mount, umount}; +use nix::errno::Errno; +use std::fs; +use std::os::unix::fs::symlink; +use std::path::Path; + +fn main() { + mount_test_ramfs(); + + let target = "/mnt/myramfs/target_file.txt"; + let symlink_path = "/mnt/myramfs/another/symlink_file.txt"; + let dir = "/mnt/myramfs/another"; + + fs::write(target, "This is the content of the target file.") + .expect("Failed to create target file"); + fs::create_dir(dir).expect("Failed to create target dir"); + + assert!(Path::new(target).exists(), "Target file was not created"); + assert!(Path::new(dir).exists(), "Target dir was not created"); + + symlink(target, symlink_path).expect("Failed to create symlink"); + + assert!(Path::new(symlink_path).exists(), "Symlink was not created"); + + let symlink_content = fs::read_link(symlink_path).expect("Failed to read symlink"); + assert_eq!( + symlink_content.display().to_string(), + target, + "Symlink points to the wrong target" + ); + + fs::remove_file(symlink_path).expect("Failed to remove symlink"); + fs::remove_file(target).expect("Failed to remove target file"); + fs::remove_dir(dir).expect("Failed to remove test_dir"); + + assert!(!Path::new(symlink_path).exists(), "Symlink was not deleted"); + assert!(!Path::new(target).exists(), "Target file was not deleted"); + assert!(!Path::new(dir).exists(), "Directory was not deleted"); + + umount_test_ramfs(); + + println!("All tests passed!"); +} + +fn mount_test_ramfs() { + let path = Path::new("mnt/myramfs"); + let dir = fs::create_dir_all(path); + assert!(dir.is_ok(), "mkdir /mnt/myramfs failed"); + + let source = b"\0".as_ptr() as *const c_char; + let target = b"/mnt/myramfs\0".as_ptr() as *const c_char; + let fstype = b"ramfs\0".as_ptr() as *const c_char; + // let flags = MS_BIND; + let flags = 0; + let data = std::ptr::null() as *const c_void; + let result = unsafe { mount(source, target, fstype, flags, data) }; + + assert_eq!( + result, + 0, + "Mount myramfs failed, errno: {}", + Errno::last().desc() + ); + println!("Mount myramfs success!"); +} + +fn umount_test_ramfs() { + let path = b"/mnt/myramfs\0".as_ptr() as *const c_char; + let result = unsafe { umount(path) }; + if result != 0 { + let err = Errno::last(); + println!("Errno: {}", err); + println!("Infomation: {}", err.desc()); + } + assert_eq!(result, 0, "Umount myramfs failed"); +} diff --git a/user/dadk/config/test_symlink_0_1_0.dadk b/user/dadk/config/test_symlink_0_1_0.dadk new file mode 100644 index 00000000..ea5b0dff --- /dev/null +++ b/user/dadk/config/test_symlink_0_1_0.dadk @@ -0,0 +1,29 @@ +{ + "name": "test-symlink", + "version": "0.1.0", + "description": "测试symlink系统调用", + "rust_target": "x86_64-unknown-dragonos", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test-symlink" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [], + "build_once": false, + "install_once": false, + "target_arch": [ + "x86_64" + ] +} \ No newline at end of file