Pārlūkot izejas kodu

新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 (#667)

* 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用
TTaq 11 mēneši atpakaļ
vecāks
revīzija
597ecc08c2

+ 17 - 4
kernel/src/filesystem/devfs/mod.rs

@@ -6,7 +6,7 @@ use super::vfs::{
     core::{generate_inode_id, ROOT_INODE},
     file::FileMode,
     syscall::ModeType,
-    FilePrivateData, FileSystem, FileType, FsInfo, IndexNode, Metadata,
+    FilePrivateData, FileSystem, FileType, FsInfo, IndexNode, Magic, Metadata, SuperBlock,
 };
 use crate::{
     driver::base::device::device_number::DeviceNumber,
@@ -25,13 +25,14 @@ use alloc::{
 };
 use system_error::SystemError;
 
-const DEVFS_MAX_NAMELEN: usize = 64;
-
+const DEVFS_BLOCK_SIZE: u64 = 512;
+const DEVFS_MAX_NAMELEN: usize = 255;
 /// @brief dev文件系统
 #[derive(Debug)]
 pub struct DevFS {
     // 文件系统根节点
     root_inode: Arc<LockedDevFSInode>,
+    super_block: SuperBlock,
 }
 
 impl FileSystem for DevFS {
@@ -53,10 +54,19 @@ impl FileSystem for DevFS {
     fn name(&self) -> &str {
         "devfs"
     }
+
+    fn super_block(&self) -> SuperBlock {
+        self.super_block.clone()
+    }
 }
 
 impl DevFS {
     pub fn new() -> Arc<Self> {
+        let super_block = SuperBlock::new(
+            Magic::DEVFS_MAGIC,
+            DEVFS_BLOCK_SIZE,
+            DEVFS_MAX_NAMELEN as u64,
+        );
         // 初始化root inode
         let root: Arc<LockedDevFSInode> = Arc::new(LockedDevFSInode(SpinLock::new(
             // /dev 的权限设置为 读+执行,root 可以读写
@@ -64,7 +74,10 @@ impl DevFS {
             DevFSInode::new(FileType::Dir, ModeType::from_bits_truncate(0o755), 0),
         )));
 
-        let devfs: Arc<DevFS> = Arc::new(DevFS { root_inode: root });
+        let devfs: Arc<DevFS> = Arc::new(DevFS {
+            root_inode: root,
+            super_block,
+        });
 
         // 对root inode加锁,并继续完成初始化工作
         let mut root_guard: SpinLockGuard<DevFSInode> = devfs.root_inode.0.lock();

+ 11 - 1
kernel/src/filesystem/fat/fs.rs

@@ -11,7 +11,7 @@ use alloc::{
 };
 
 use crate::driver::base::device::device_number::DeviceNumber;
-use crate::filesystem::vfs::SpecialNodeData;
+use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock};
 use crate::ipc::pipe::LockedPipeInode;
 use crate::{
     driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom},
@@ -36,6 +36,8 @@ use super::{
     utils::RESERVED_CLUSTERS,
 };
 
+const FAT_MAX_NAMELEN: u64 = 255;
+
 /// FAT32文件系统的最大的文件大小
 pub const MAX_FILE_SIZE: u64 = 0xffff_ffff;
 
@@ -252,6 +254,14 @@ impl FileSystem for FATFileSystem {
     fn name(&self) -> &str {
         "fat"
     }
+
+    fn super_block(&self) -> SuperBlock {
+        SuperBlock::new(
+            Magic::FAT_MAGIC,
+            self.bpb.bytes_per_sector.into(),
+            FAT_MAX_NAMELEN,
+        )
+    }
 }
 
 impl FATFileSystem {

+ 10 - 2
kernel/src/filesystem/kernfs/mod.rs

@@ -22,7 +22,7 @@ use self::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
 
 use super::vfs::{
     core::generate_inode_id, file::FileMode, syscall::ModeType, FilePrivateData, FileSystem,
-    FileType, FsInfo, IndexNode, InodeId, Metadata,
+    FileType, FsInfo, IndexNode, InodeId, Magic, Metadata, SuperBlock,
 };
 
 pub mod callback;
@@ -51,11 +51,19 @@ impl FileSystem for KernFS {
     fn name(&self) -> &str {
         "kernfs"
     }
+
+    fn super_block(&self) -> SuperBlock {
+        SuperBlock::new(
+            Magic::KER_MAGIC,
+            KernFS::KERNFS_BLOCK_SIZE,
+            KernFS::MAX_NAMELEN as u64,
+        )
+    }
 }
 
 impl KernFS {
     pub const MAX_NAMELEN: usize = 4096;
-
+    pub const KERNFS_BLOCK_SIZE: u64 = 512;
     #[allow(dead_code)]
     pub fn new() -> Arc<Self> {
         let root_inode = Self::create_root_inode();

+ 17 - 3
kernel/src/filesystem/procfs/mod.rs

@@ -20,6 +20,7 @@ use crate::{
     kerror, kinfo,
     libs::{
         once::Once,
+        rwlock::RwLock,
         spinlock::{SpinLock, SpinLockGuard},
     },
     mm::allocator::page_frame::FrameAllocator,
@@ -30,7 +31,7 @@ use crate::{
 use super::vfs::{
     file::{FileMode, FilePrivateData},
     syscall::ModeType,
-    FileSystem, FsInfo, IndexNode, InodeId, Metadata,
+    FileSystem, FsInfo, IndexNode, InodeId, Magic, Metadata, SuperBlock,
 };
 
 pub mod kmsg;
@@ -76,7 +77,7 @@ pub struct InodeInfo {
 
 /// @brief procfs的inode名称的最大长度
 const PROCFS_MAX_NAMELEN: usize = 64;
-
+const PROCFS_BLOCK_SIZE: u64 = 512;
 /// @brief procfs文件系统的Inode结构体
 #[derive(Debug)]
 pub struct LockedProcFSInode(SpinLock<ProcFSInode>);
@@ -86,6 +87,7 @@ pub struct LockedProcFSInode(SpinLock<ProcFSInode>);
 pub struct ProcFS {
     /// procfs的root inode
     root_inode: Arc<LockedProcFSInode>,
+    super_block: RwLock<SuperBlock>,
 }
 
 #[derive(Debug, Clone)]
@@ -290,10 +292,19 @@ impl FileSystem for ProcFS {
     fn name(&self) -> &str {
         "procfs"
     }
+
+    fn super_block(&self) -> SuperBlock {
+        self.super_block.read().clone()
+    }
 }
 
 impl ProcFS {
     pub fn new() -> Arc<Self> {
+        let super_block = SuperBlock::new(
+            Magic::PROC_MAGIC,
+            PROCFS_BLOCK_SIZE,
+            PROCFS_MAX_NAMELEN as u64,
+        );
         // 初始化root inode
         let root: Arc<LockedProcFSInode> =
             Arc::new(LockedProcFSInode(SpinLock::new(ProcFSInode {
@@ -324,7 +335,10 @@ impl ProcFS {
                 },
             })));
 
-        let result: Arc<ProcFS> = Arc::new(ProcFS { root_inode: root });
+        let result: Arc<ProcFS> = Arc::new(ProcFS {
+            root_inode: root,
+            super_block: RwLock::new(super_block),
+        });
 
         // 对root inode加锁,并继续完成初始化工作
         let mut root_guard: SpinLockGuard<ProcFSInode> = result.root_inode.0.lock();

+ 17 - 2
kernel/src/filesystem/ramfs/mod.rs

@@ -2,6 +2,7 @@ use core::any::Any;
 use core::intrinsics::unlikely;
 
 use crate::filesystem::vfs::FSMAKER;
+use crate::libs::rwlock::RwLock;
 use crate::{
     driver::base::device::device_number::DeviceNumber,
     filesystem::vfs::{core::generate_inode_id, FileType},
@@ -21,10 +22,11 @@ use super::vfs::{
     file::FilePrivateData, syscall::ModeType, FileSystem, FileSystemMaker, FsInfo, IndexNode,
     InodeId, Metadata, SpecialNodeData,
 };
+use super::vfs::{Magic, SuperBlock};
 
 /// RamFS的inode名称的最大长度
 const RAMFS_MAX_NAMELEN: usize = 64;
-
+const RAMFS_BLOCK_SIZE: u64 = 512;
 /// @brief 内存文件系统的Inode结构体
 #[derive(Debug)]
 struct LockedRamFSInode(SpinLock<RamFSInode>);
@@ -34,6 +36,7 @@ struct LockedRamFSInode(SpinLock<RamFSInode>);
 pub struct RamFS {
     /// RamFS的root inode
     root_inode: Arc<LockedRamFSInode>,
+    super_block: RwLock<SuperBlock>,
 }
 
 /// @brief 内存文件系统的Inode结构体(不包含锁)
@@ -80,10 +83,19 @@ impl FileSystem for RamFS {
     fn name(&self) -> &str {
         "ramfs"
     }
+
+    fn super_block(&self) -> SuperBlock {
+        self.super_block.read().clone()
+    }
 }
 
 impl RamFS {
     pub fn new() -> Arc<Self> {
+        let super_block = SuperBlock::new(
+            Magic::RAMFS_MAGIC,
+            RAMFS_BLOCK_SIZE,
+            RAMFS_MAX_NAMELEN as u64,
+        );
         // 初始化root inode
         let root: Arc<LockedRamFSInode> = Arc::new(LockedRamFSInode(SpinLock::new(RamFSInode {
             parent: Weak::default(),
@@ -110,7 +122,10 @@ impl RamFS {
             special_node: None,
         })));
 
-        let result: Arc<RamFS> = Arc::new(RamFS { root_inode: root });
+        let result: Arc<RamFS> = Arc::new(RamFS {
+            root_inode: root,
+            super_block: RwLock::new(super_block),
+        });
 
         // 对root inode加锁,并继续完成初始化工作
         let mut root_guard: SpinLockGuard<RamFSInode> = result.root_inode.0.lock();

+ 56 - 0
kernel/src/filesystem/vfs/mod.rs

@@ -584,6 +584,60 @@ impl Default for Metadata {
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct SuperBlock {
+    // type of filesystem
+    pub magic: Magic,
+    // optimal transfer block size
+    pub bsize: u64,
+    // total data blocks in filesystem
+    pub blocks: u64,
+    // free block in system
+    pub bfree: u64,
+    // 可供非特权用户使用的空闲块
+    pub bavail: u64,
+    // total inodes in filesystem
+    pub files: u64,
+    // free inodes in filesystem
+    pub ffree: u64,
+    // filesysytem id
+    pub fsid: u64,
+    // Max length of filename
+    pub namelen: u64,
+    // fragment size
+    pub frsize: u64,
+    // mount flags of filesystem
+    pub flags: u64,
+}
+
+impl SuperBlock {
+    pub fn new(magic: Magic, bsize: u64, namelen: u64) -> Self {
+        Self {
+            magic,
+            bsize,
+            blocks: 0,
+            bfree: 0,
+            bavail: 0,
+            files: 0,
+            ffree: 0,
+            fsid: 0,
+            namelen,
+            frsize: 0,
+            flags: 0,
+        }
+    }
+}
+bitflags! {
+    pub struct Magic: u64 {
+        const DEVFS_MAGIC = 0x1373;
+        const FAT_MAGIC =  0xf2f52011;
+        const KER_MAGIC = 0x3153464b;
+        const PROC_MAGIC = 0x9fa0;
+        const RAMFS_MAGIC = 0x858458f6;
+        const MOUNT_MAGIC = 61267;
+    }
+}
+
 /// @brief 所有文件系统都应该实现的trait
 pub trait FileSystem: Any + Sync + Send + Debug {
     /// @brief 获取当前文件系统的root inode的指针
@@ -597,6 +651,8 @@ pub trait FileSystem: Any + Sync + Send + Debug {
     fn as_any_ref(&self) -> &dyn Any;
 
     fn name(&self) -> &str;
+
+    fn super_block(&self) -> SuperBlock;
 }
 
 impl DowncastArc for dyn FileSystem {

+ 6 - 0
kernel/src/filesystem/vfs/mount.rs

@@ -13,8 +13,11 @@ use crate::{driver::base::device::device_number::DeviceNumber, libs::spinlock::S
 
 use super::{
     file::FileMode, syscall::ModeType, FilePrivateData, FileSystem, FileType, IndexNode, InodeId,
+    Magic, SuperBlock,
 };
 
+const MOUNTFS_BLOCK_SIZE: u64 = 512;
+const MOUNTFS_MAX_NAMELEN: u64 = 64;
 /// @brief 挂载文件系统
 /// 挂载文件系统的时候,套了MountFS这一层,以实现文件系统的递归挂载
 #[derive(Debug)]
@@ -398,4 +401,7 @@ impl FileSystem for MountFS {
     fn name(&self) -> &str {
         "mountfs"
     }
+    fn super_block(&self) -> SuperBlock {
+        SuperBlock::new(Magic::MOUNT_MAGIC, MOUNTFS_BLOCK_SIZE, MOUNTFS_MAX_NAMELEN)
+    }
 }

+ 66 - 0
kernel/src/filesystem/vfs/syscall.rs

@@ -20,6 +20,7 @@ use crate::{
     time::TimeSpec,
 };
 
+use super::SuperBlock;
 use super::{
     core::{do_mkdir, do_remove_dir, do_unlink_at},
     fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC},
@@ -323,6 +324,41 @@ bitflags! {
     }
 }
 
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct PosixStatfs {
+    f_type: u64,
+    f_bsize: u64,
+    f_blocks: u64,
+    f_bfree: u64,
+    f_bavail: u64,
+    f_files: u64,
+    f_ffree: u64,
+    f_fsid: u64,
+    f_namelen: u64,
+    f_frsize: u64,
+    f_flags: u64,
+    f_spare: [u64; 4],
+}
+
+impl From<SuperBlock> for PosixStatfs {
+    fn from(super_block: SuperBlock) -> Self {
+        Self {
+            f_type: super_block.magic.bits,
+            f_bsize: super_block.bsize,
+            f_blocks: super_block.blocks,
+            f_bfree: super_block.bfree,
+            f_bavail: super_block.bavail,
+            f_files: super_block.files,
+            f_ffree: super_block.ffree,
+            f_fsid: super_block.fsid,
+            f_namelen: super_block.namelen,
+            f_frsize: super_block.frsize,
+            f_flags: super_block.flags,
+            f_spare: [0u64; 4],
+        }
+    }
+}
 ///
 ///  Arguments for how openat2(2) should open the target path. If only @flags and
 ///  @mode are non-zero, then openat2(2) operates very similarly to openat(2).
@@ -1209,6 +1245,36 @@ impl Syscall {
         return r;
     }
 
+    pub fn statfs(path: *const u8, user_statfs: *mut PosixStatfs) -> Result<usize, SystemError> {
+        let mut writer = UserBufferWriter::new(user_statfs, size_of::<PosixStatfs>(), true)?;
+        let fd = Self::open(
+            path,
+            FileMode::O_RDONLY.bits(),
+            ModeType::empty().bits(),
+            true,
+        )?;
+        let path = check_and_clone_cstr(path, Some(MAX_PATHLEN)).unwrap();
+        let pcb = ProcessManager::current_pcb();
+        let (_inode_begin, remain_path) = user_path_at(&pcb, fd as i32, &path)?;
+        let inode = ROOT_INODE().lookup_follow_symlink(&remain_path, MAX_PATHLEN)?;
+        let statfs = PosixStatfs::from(inode.fs().super_block());
+        writer.copy_one_to_user(&statfs, 0)?;
+        return Ok(0);
+    }
+
+    pub fn fstatfs(fd: i32, user_statfs: *mut PosixStatfs) -> Result<usize, SystemError> {
+        let mut writer = UserBufferWriter::new(user_statfs, size_of::<PosixStatfs>(), true)?;
+        let binding = ProcessManager::current_pcb().fd_table();
+        let fd_table_guard = binding.read();
+        let file = fd_table_guard
+            .get_file_by_fd(fd)
+            .ok_or(SystemError::EBADF)?;
+        drop(fd_table_guard);
+        let statfs = PosixStatfs::from(file.lock().inode().fs().super_block());
+        writer.copy_one_to_user(&statfs, 0)?;
+        return Ok(0);
+    }
+
     pub fn do_statx(
         fd: i32,
         path: *const u8,

+ 13 - 1
kernel/src/syscall/mod.rs

@@ -6,7 +6,7 @@ use core::{
 
 use crate::{
     arch::{ipc::signal::SigSet, syscall::nr::*},
-    filesystem::vfs::syscall::PosixStatx,
+    filesystem::vfs::syscall::{PosixStatfs, PosixStatx},
     libs::{futex::constant::FutexFlag, rand::GRandFlags},
     mm::syscall::MremapFlags,
     net::syscall::MsgHdr,
@@ -721,6 +721,18 @@ impl Syscall {
                 Self::stat(path, kstat)
             }
 
+            SYS_STATFS => {
+                let path = args[0] as *const u8;
+                let statfs = args[1] as *mut PosixStatfs;
+                Self::statfs(path, statfs)
+            }
+
+            SYS_FSTATFS => {
+                let fd = args[0] as i32;
+                let statfs = args[1] as *mut PosixStatfs;
+                Self::fstatfs(fd, statfs)
+            }
+
             SYS_STATX => {
                 let fd = args[0] as i32;
                 let path = args[1] as *const u8;

+ 1 - 0
user/apps/test_fstatfs/.gitignore

@@ -0,0 +1 @@
+test_fstatfs

+ 20 - 0
user/apps/test_fstatfs/Makefile

@@ -0,0 +1,20 @@
+ifeq ($(ARCH), x86_64)
+	CROSS_COMPILE=x86_64-linux-musl-
+else ifeq ($(ARCH), riscv64)
+	CROSS_COMPILE=riscv64-linux-musl-
+endif
+
+CC=$(CROSS_COMPILE)gcc
+
+.PHONY: all
+all: main.c
+	$(CC) -static -o test_fstatfs main.c
+
+.PHONY: install clean
+install: all
+	mv test_fstatfs $(DADK_CURRENT_BUILD_DIR)/test_fstatfs
+
+clean:
+	rm test_fstatfs *.o
+
+fmt:

+ 41 - 0
user/apps/test_fstatfs/main.c

@@ -0,0 +1,41 @@
+#include <sys/statfs.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+int main(int argc,char **argv)
+{
+    int fd = open("/bin/about.elf", O_RDONLY);
+    if (fd == -1)
+        return 0;
+    printf("fd = %d\n", fd);
+    struct statfs diskInfo;
+
+    
+    fstatfs(fd, &diskInfo);
+    unsigned long long blocksize1 = diskInfo.f_bsize;    //每个block里包含的字节数
+    unsigned long long totalsize = blocksize1 * diskInfo.f_blocks;//总的字节数,f_blocks为block的数目
+    printf("Total_size=%llu B =%llu KB =%llu MB = %llu GB\n",
+           totalsize,totalsize>>10,totalsize>>20, totalsize>>30);
+
+    /* 2.获取一下剩余空间和可用空间的大小 */
+    unsigned long long freeDisk = diskInfo.f_bfree * blocksize1;  //剩余空间的大小 
+    unsigned long long availableDisk = diskInfo.f_bavail * blocksize1; //可用空间大小
+    printf("Disk_free=%llu MB =%llu GB Disk_available=%llu MB = %llu GB\n",
+           freeDisk>>20,freeDisk>>30,availableDisk>>20, availableDisk>>30);
+
+
+    printf("====================\n");
+    printf("diskInfo address: %p\n", diskInfo);
+    printf("f_type= %lu\n", diskInfo.f_type);
+    printf("f_bsize = %lu\n", diskInfo.f_bsize);
+    printf("f_blocks = %d\n", diskInfo.f_blocks);
+    printf("f_bfree = %lu\n", diskInfo.f_bfree);
+    printf("b_avail = %d\n", diskInfo.f_bavail);
+    printf("f_files = %d\n", diskInfo.f_files);
+    printf("f_ffree = %lu\n", diskInfo.f_ffree);
+    printf("f_fsid = %ld\n", diskInfo.f_fsid);
+    printf("f_namelen = %ld\n", diskInfo.f_namelen);
+    printf("f_frsize = %ld\n", diskInfo.f_frsize);
+    printf("f_flags = %ld\n", diskInfo.f_flags);
+    return 0;
+}

+ 1 - 0
user/apps/test_statfs/.gitignore

@@ -0,0 +1 @@
+test_statfs

+ 20 - 0
user/apps/test_statfs/Makefile

@@ -0,0 +1,20 @@
+ifeq ($(ARCH), x86_64)
+	CROSS_COMPILE=x86_64-linux-musl-
+else ifeq ($(ARCH), riscv64)
+	CROSS_COMPILE=riscv64-linux-musl-
+endif
+
+CC=$(CROSS_COMPILE)gcc
+
+.PHONY: all
+all: main.c
+	$(CC) -static -o test_statfs main.c
+
+.PHONY: install clean
+install: all
+	mv test_statfs $(DADK_CURRENT_BUILD_DIR)/test_statfs
+
+clean:
+	rm test_statfs *.o
+
+fmt:

+ 36 - 0
user/apps/test_statfs/main.c

@@ -0,0 +1,36 @@
+#include <sys/statfs.h>
+#include <stdio.h>
+
+int main(int argc,char **argv)
+{
+    struct statfs diskInfo;
+
+    
+    statfs("/bin/about.elf", &diskInfo);
+    unsigned long long blocksize1 = diskInfo.f_bsize;    //每个block里包含的字节数
+    unsigned long long totalsize = blocksize1 * diskInfo.f_blocks;//总的字节数,f_blocks为block的数目
+    printf("Total_size=%llu B =%llu KB =%llu MB = %llu GB\n",
+           totalsize,totalsize>>10,totalsize>>20, totalsize>>30);
+
+    /* 2.获取一下剩余空间和可用空间的大小 */
+    unsigned long long freeDisk = diskInfo.f_bfree * blocksize1;  //剩余空间的大小 
+    unsigned long long availableDisk = diskInfo.f_bavail * blocksize1; //可用空间大小
+    printf("Disk_free=%llu MB =%llu GB Disk_available=%llu MB = %llu GB\n",
+           freeDisk>>20,freeDisk>>30,availableDisk>>20, availableDisk>>30);
+
+
+    printf("====================\n");
+    printf("diskInfo address: %p\n", diskInfo);
+    printf("f_type= %lu\n", diskInfo.f_type);
+    printf("f_bsize = %lu\n", diskInfo.f_bsize);
+    printf("f_blocks = %d\n", diskInfo.f_blocks);
+    printf("f_bfree = %lu\n", diskInfo.f_bfree);
+    printf("b_avail = %d\n", diskInfo.f_bavail);
+    printf("f_files = %d\n", diskInfo.f_files);
+    printf("f_ffree = %lu\n", diskInfo.f_ffree);
+    printf("f_fsid = %ld\n", diskInfo.f_fsid);
+    printf("f_namelen = %ld\n", diskInfo.f_namelen);
+    printf("f_frsize = %ld\n", diskInfo.f_frsize);
+    printf("f_flags = %ld\n", diskInfo.f_flags);
+    return 0;
+}

+ 26 - 0
user/dadk/config/test_fstatfs_0_1_0.dadk

@@ -0,0 +1,26 @@
+{
+  "name": "test_fstatfs",
+  "version": "0.1.0",
+  "description": "测试fstatfs",
+  "rust_target": null,
+  "task_type": {
+    "BuildFromSource": {
+      "Local": {
+        "path": "apps/test_fstatfs"
+      }
+    }
+  },
+  "depends": [],
+  "build": {
+    "build_command": "make install"
+  },
+  "install": {
+    "in_dragonos_path": "/bin"
+  },
+  "clean": {
+    "clean_command": "make clean"
+  },
+  "envs": [],
+  "build_once": false,
+  "install_once": false
+}

+ 26 - 0
user/dadk/config/test_statfs_0_1_0.dadk

@@ -0,0 +1,26 @@
+{
+  "name": "test_statfs",
+  "version": "0.1.0",
+  "description": "测试statfs",
+  "rust_target": null,
+  "task_type": {
+    "BuildFromSource": {
+      "Local": {
+        "path": "apps/test_statfs"
+      }
+    }
+  },
+  "depends": [],
+  "build": {
+    "build_command": "make install"
+  },
+  "install": {
+    "in_dragonos_path": "/bin"
+  },
+  "clean": {
+    "clean_command": "make clean"
+  },
+  "envs": [],
+  "build_once": false,
+  "install_once": false
+}