Przeglądaj źródła

实现系统调用Fstat (#295)

* fstat

* 修改syscall.rs中的verify_area
houmkh 1 rok temu
rodzic
commit
be63f3b2b6

+ 1 - 0
.vscode/c_cpp_properties.json

@@ -4,6 +4,7 @@
             "name": "DragonOS",
             "includePath": [
                 "${workspaceFolder}/**",
+                "${workspaceFolder}/bin/sysroot/usr/include",
                 "${workspaceFolder}/user/libs/libc/src/include",
                 "${workspaceFolder}/user/libs/libc/src/include/export"
             ],

+ 1 - 0
kernel/src/filesystem/mod.rs

@@ -5,3 +5,4 @@ pub mod procfs;
 pub mod ramfs;
 pub mod sysfs;
 pub mod vfs;
+pub mod syscall;

+ 167 - 0
kernel/src/filesystem/syscall.rs

@@ -0,0 +1,167 @@
+use crate::{
+    arch::asm::current::current_pcb,
+    filesystem::vfs::FileType,
+    kdebug,
+    syscall::{Syscall, SystemError},
+    time::TimeSpec,
+};
+
+bitflags! {
+    /// 文件类型和权限
+    pub struct ModeType: u32 {
+        /// 掩码
+        const S_IFMT = 0o0_170_000;
+        /// 文件类型
+        const S_IFSOCK = 0o140000;
+        const S_IFLNK = 0o120000;
+        const S_IFREG = 0o100000;
+        const S_IFBLK = 0o060000;
+        const S_IFDIR = 0o040000;
+        const S_IFCHR = 0o020000;
+        const S_IFIFO = 0o010000;
+
+        const S_ISUID = 0o004000;
+        const S_ISGID = 0o002000;
+        const S_ISVTX = 0o001000;
+        /// 文件用户权限
+        const S_IRWXU = 0o0700;
+        const S_IRUSR = 0o0400;
+        const S_IWUSR = 0o0200;
+        const S_IXUSR = 0o0100;
+        /// 文件组权限
+        const S_IRWXG = 0o0070;
+        const S_IRGRP = 0o0040;
+        const S_IWGRP = 0o0020;
+        const S_IXGRP = 0o0010;
+        /// 文件其他用户权限
+        const S_IRWXO = 0o0007;
+        const S_IROTH = 0o0004;
+        const S_IWOTH = 0o0002;
+        const S_IXOTH = 0o0001;
+    }
+}
+
+#[repr(C)]
+/// # 文件信息结构体
+pub struct PosixKstat {
+    /// 硬件设备ID
+    dev_id: u64,
+    /// inode号
+    inode: u64,
+    /// 硬链接数
+    nlink: u64,
+    /// 文件权限
+    mode: ModeType,
+    /// 所有者用户ID
+    uid: i32,
+    /// 所有者组ID
+    gid: i32,
+    /// 设备ID
+    rdev: i64,
+    /// 文件大小
+    size: i64,
+    /// 文件系统块大小
+    blcok_size: i64,
+    /// 分配的512B块数
+    blocks: u64,
+    /// 最后访问时间
+    atime: TimeSpec,
+    /// 最后修改时间
+    mtime: TimeSpec,
+    /// 最后状态变化时间
+    ctime: TimeSpec,
+    /// 用于填充结构体大小的空白数据
+    pub _pad: [i8; 24],
+}
+impl PosixKstat {
+    fn new() -> Self {
+        Self {
+            inode: 0,
+            dev_id: 0,
+            mode: ModeType { bits: 0 },
+            nlink: 0,
+            uid: 0,
+            gid: 0,
+            rdev: 0,
+            size: 0,
+            atime: TimeSpec {
+                tv_sec: 0,
+                tv_nsec: 0,
+            },
+            mtime: TimeSpec {
+                tv_sec: 0,
+                tv_nsec: 0,
+            },
+            ctime: TimeSpec {
+                tv_sec: 0,
+                tv_nsec: 0,
+            },
+            blcok_size: 0,
+            blocks: 0,
+            _pad: Default::default(),
+        }
+    }
+}
+impl Syscall {
+    fn do_fstat(fd: i32) -> Result<PosixKstat, SystemError> {
+        let cur = current_pcb();
+        match cur.get_file_ref_by_fd(fd) {
+            Some(file) => {
+                let mut kstat = PosixKstat::new();
+                // 获取文件信息
+                match file.metadata() {
+                    Ok(metadata) => {
+                        kstat.size = metadata.size as i64;
+                        kstat.dev_id = metadata.dev_id as u64;
+                        kstat.inode = metadata.inode_id as u64;
+                        kstat.blcok_size = metadata.blk_size as i64;
+                        kstat.blocks = metadata.blocks as u64;
+
+                        kstat.atime.tv_sec = metadata.atime.tv_sec;
+                        kstat.atime.tv_nsec = metadata.atime.tv_nsec;
+                        kstat.mtime.tv_sec = metadata.mtime.tv_sec;
+                        kstat.mtime.tv_nsec = metadata.mtime.tv_nsec;
+                        kstat.ctime.tv_sec = metadata.ctime.tv_sec;
+                        kstat.ctime.tv_nsec = metadata.ctime.tv_nsec;
+
+                        kstat.nlink = metadata.nlinks as u64;
+                        kstat.uid = metadata.uid as i32;
+                        kstat.gid = metadata.gid as i32;
+                        kstat.rdev = metadata.raw_dev as i64;
+                        kstat.mode.bits = metadata.mode;
+                        match file.file_type() {
+                            FileType::File => kstat.mode.insert(ModeType::S_IFMT),
+                            FileType::Dir => kstat.mode.insert(ModeType::S_IFDIR),
+                            FileType::BlockDevice => kstat.mode.insert(ModeType::S_IFBLK),
+                            FileType::CharDevice => kstat.mode.insert(ModeType::S_IFCHR),
+                            FileType::SymLink => kstat.mode.insert(ModeType::S_IFLNK),
+                            FileType::Socket => kstat.mode.insert(ModeType::S_IFSOCK),
+                            FileType::Pipe => kstat.mode.insert(ModeType::S_IFIFO),
+                        }
+                    }
+                    Err(e) => return Err(e),
+                }
+
+                return Ok(kstat);
+            }
+            None => {
+                kdebug!("file not be opened");
+                return Err(SystemError::EINVAL);
+            }
+        }
+    }
+    pub fn fstat(fd: i32, usr_kstat: *mut PosixKstat) -> Result<usize, SystemError> {
+        match Self::do_fstat(fd) {
+            Ok(kstat) => {
+                if usr_kstat.is_null() {
+                    return Err(SystemError::EFAULT);
+                }
+                unsafe {
+                    *usr_kstat = kstat;
+                }
+                return Ok(0);
+            }
+            Err(e) => return Err(e),
+        }
+    }
+}

+ 84 - 64
kernel/src/syscall/mod.rs

@@ -7,16 +7,17 @@ use num_traits::{FromPrimitive, ToPrimitive};
 
 use crate::{
     arch::{cpu::cpu_reset, MMArch},
+    filesystem::syscall::PosixKstat,
     filesystem::vfs::{
         file::FileMode,
         syscall::{SEEK_CUR, SEEK_END, SEEK_MAX, SEEK_SET},
         MAX_PATHLEN,
     },
-    include::bindings::bindings::{pid_t, verify_area, PAGE_2M_SIZE, PAGE_4K_SIZE},
+    include::bindings::bindings::{pid_t, PAGE_2M_SIZE, PAGE_4K_SIZE},
     io::SeekFrom,
     kinfo,
     libs::align::page_align_up,
-    mm::{MemoryManagementArch, VirtAddr},
+    mm::{verify_area, MemoryManagementArch, VirtAddr},
     net::syscall::SockAddr,
     time::{
         syscall::{PosixTimeZone, PosixTimeval},
@@ -364,6 +365,8 @@ pub const SYS_MMAP: usize = 44;
 pub const SYS_MUNMAP: usize = 45;
 pub const SYS_MPROTECT: usize = 46;
 
+pub const SYS_FSTAT: usize = 47;
+
 #[derive(Debug)]
 pub struct Syscall;
 
@@ -421,9 +424,9 @@ impl Syscall {
                 let fd = args[0] as i32;
                 let buf_vaddr = args[1];
                 let len = args[2];
-
+                let virt_addr = VirtAddr::new(buf_vaddr);
                 // 判断缓冲区是否来自用户态,进行权限校验
-                let res = if from_user && unsafe { !verify_area(buf_vaddr as u64, len as u64) } {
+                let res = if from_user && verify_area(virt_addr, len as usize).is_err() {
                     // 来自用户态,而buffer在内核态,这样的操作不被允许
                     Err(SystemError::EPERM)
                 } else {
@@ -439,9 +442,9 @@ impl Syscall {
                 let fd = args[0] as i32;
                 let buf_vaddr = args[1];
                 let len = args[2];
-
+                let virt_addr = VirtAddr::new(buf_vaddr);
                 // 判断缓冲区是否来自用户态,进行权限校验
-                let res = if from_user && unsafe { !verify_area(buf_vaddr as u64, len as u64) } {
+                let res = if from_user && verify_area(virt_addr, len as usize).is_err() {
                     // 来自用户态,而buffer在内核态,这样的操作不被允许
                     Err(SystemError::EPERM)
                 } else {
@@ -496,10 +499,10 @@ impl Syscall {
                         return Err(SystemError::EFAULT);
                     }
                     let path_ptr = arg0 as *const c_char;
+                    let virt_addr = VirtAddr::new(path_ptr as usize);
                     // 权限校验
                     if path_ptr.is_null()
-                        || (from_user
-                            && unsafe { !verify_area(path_ptr as u64, PAGE_2M_SIZE as u64) })
+                        || (from_user && verify_area(virt_addr, PAGE_2M_SIZE as usize).is_err())
                     {
                         return Err(SystemError::EINVAL);
                     }
@@ -526,9 +529,9 @@ impl Syscall {
                 let fd = args[0] as i32;
                 let buf_vaddr = args[1];
                 let len = args[2];
-
+                let virt_addr = VirtAddr::new(buf_vaddr);
                 // 判断缓冲区是否来自用户态,进行权限校验
-                let res = if from_user && unsafe { !verify_area(buf_vaddr as u64, len as u64) } {
+                let res = if from_user && verify_area(virt_addr, len as usize).is_err() {
                     // 来自用户态,而buffer在内核态,这样的操作不被允许
                     Err(SystemError::EPERM)
                 } else if buf_vaddr == 0 {
@@ -547,12 +550,14 @@ impl Syscall {
                 let path_ptr = args[0];
                 let argv_ptr = args[1];
                 let env_ptr = args[2];
-
+                let virt_path_ptr = VirtAddr::new(path_ptr);
+                let virt_argv_ptr = VirtAddr::new(argv_ptr);
+                let virt_env_ptr = VirtAddr::new(env_ptr);
                 // 权限校验
                 if from_user
-                    && (unsafe { !verify_area(path_ptr as u64, PAGE_4K_SIZE as u64) }
-                        || unsafe { !verify_area(argv_ptr as u64, PAGE_4K_SIZE as u64) })
-                    || unsafe { !verify_area(env_ptr as u64, PAGE_4K_SIZE as u64) }
+                    && (verify_area(virt_path_ptr, PAGE_4K_SIZE as usize).is_err()
+                        || verify_area(virt_argv_ptr, PAGE_4K_SIZE as usize).is_err())
+                    || verify_area(virt_env_ptr, PAGE_4K_SIZE as usize).is_err()
                 {
                     Err(SystemError::EFAULT)
                 } else {
@@ -568,13 +573,13 @@ impl Syscall {
                 let wstatus = args[1] as *mut c_int;
                 let options = args[2] as c_int;
                 let rusage = args[3] as *mut c_void;
-
+                let virt_wstatus = VirtAddr::new(wstatus as usize);
+                let virt_rusage = VirtAddr::new(rusage as usize);
                 // 权限校验
                 // todo: 引入rusage之后,更正以下权限校验代码中,rusage的大小
                 if from_user
-                    && (unsafe {
-                        !verify_area(wstatus as u64, core::mem::size_of::<c_int>() as u64)
-                    } || unsafe { !verify_area(rusage as u64, PAGE_4K_SIZE as u64) })
+                    && (verify_area(virt_wstatus, core::mem::size_of::<c_int>() as usize).is_err()
+                        || verify_area(virt_rusage, PAGE_4K_SIZE as usize).is_err())
                 {
                     Err(SystemError::EFAULT)
                 } else {
@@ -589,11 +594,10 @@ impl Syscall {
             SYS_MKDIR => {
                 let path_ptr = args[0] as *const c_char;
                 let mode = args[1];
-
+                let virt_path_ptr = VirtAddr::new(path_ptr as usize);
                 let security_check = || {
                     if path_ptr.is_null()
-                        || (from_user
-                            && unsafe { !verify_area(path_ptr as u64, PAGE_2M_SIZE as u64) })
+                        || (from_user && verify_area(virt_path_ptr, PAGE_2M_SIZE as usize).is_err())
                     {
                         return Err(SystemError::EINVAL);
                     }
@@ -617,12 +621,12 @@ impl Syscall {
             SYS_NANOSLEEP => {
                 let req = args[0] as *const TimeSpec;
                 let rem = args[1] as *mut TimeSpec;
+                let virt_req = VirtAddr::new(req as usize);
+                let virt_rem = VirtAddr::new(rem as usize);
                 if from_user
-                    && (unsafe {
-                        !verify_area(req as u64, core::mem::size_of::<TimeSpec>() as u64)
-                    } || unsafe {
-                        !verify_area(rem as u64, core::mem::size_of::<TimeSpec>() as u64)
-                    })
+                    && (verify_area(virt_req, core::mem::size_of::<TimeSpec>() as usize).is_err()
+                        || verify_area(virt_rem, core::mem::size_of::<TimeSpec>() as usize)
+                            .is_err())
                 {
                     Err(SystemError::EFAULT)
                 } else {
@@ -633,10 +637,10 @@ impl Syscall {
             SYS_CLOCK => Self::clock(),
             SYS_PIPE => {
                 let pipefd = args[0] as *mut c_int;
+                let virt_pipefd = VirtAddr::new(pipefd as usize);
                 if from_user
-                    && unsafe {
-                        !verify_area(pipefd as u64, core::mem::size_of::<[c_int; 2]>() as u64)
-                    }
+                    && verify_area(virt_pipefd, core::mem::size_of::<[c_int; 2]>() as usize)
+                        .is_err()
                 {
                     Err(SystemError::EFAULT)
                 } else if pipefd.is_null() {
@@ -651,7 +655,8 @@ impl Syscall {
                 let dirfd = args[0] as i32;
                 let pathname = args[1] as *const c_char;
                 let flags = args[2] as u32;
-                if from_user && unsafe { !verify_area(pathname as u64, PAGE_4K_SIZE as u64) } {
+                let virt_pathname = VirtAddr::new(pathname as usize);
+                if from_user && verify_area(virt_pathname, PAGE_4K_SIZE as usize).is_err() {
                     Err(SystemError::EFAULT)
                 } else if pathname.is_null() {
                     Err(SystemError::EFAULT)
@@ -710,8 +715,9 @@ impl Syscall {
             SYS_SETSOCKOPT => {
                 let optval = args[3] as *const u8;
                 let optlen = args[4] as usize;
+                let virt_optval = VirtAddr::new(optval as usize);
                 // 验证optval的地址是否合法
-                if unsafe { verify_area(optval as u64, optlen as u64) } == false {
+                if verify_area(virt_optval, optlen as usize).is_err() {
                     // 地址空间超出了用户空间的范围,不合法
                     Err(SystemError::EFAULT)
                 } else {
@@ -722,18 +728,17 @@ impl Syscall {
             SYS_GETSOCKOPT => {
                 let optval = args[3] as *mut u8;
                 let optlen = args[4] as *mut usize;
-
+                let virt_optval = VirtAddr::new(optval as usize);
+                let virt_optlen = VirtAddr::new(optlen as usize);
                 let security_check = || {
                     // 验证optval的地址是否合法
-                    if unsafe { verify_area(optval as u64, PAGE_4K_SIZE as u64) } == false {
+                    if verify_area(virt_optval, PAGE_4K_SIZE as usize).is_err() {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
                     }
 
                     // 验证optlen的地址是否合法
-                    if unsafe { verify_area(optlen as u64, core::mem::size_of::<u32>() as u64) }
-                        == false
-                    {
+                    if verify_area(virt_optlen, core::mem::size_of::<u32>() as usize).is_err() {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
                     }
@@ -750,8 +755,9 @@ impl Syscall {
             SYS_CONNECT => {
                 let addr = args[1] as *const SockAddr;
                 let addrlen = args[2] as usize;
+                let virt_addr = VirtAddr::new(addr as usize);
                 // 验证addr的地址是否合法
-                if unsafe { verify_area(addr as u64, addrlen as u64) } == false {
+                if verify_area(virt_addr, addrlen as usize).is_err() {
                     // 地址空间超出了用户空间的范围,不合法
                     Err(SystemError::EFAULT)
                 } else {
@@ -761,8 +767,9 @@ impl Syscall {
             SYS_BIND => {
                 let addr = args[1] as *const SockAddr;
                 let addrlen = args[2] as usize;
+                let virt_addr = VirtAddr::new(addr as usize);
                 // 验证addr的地址是否合法
-                if unsafe { verify_area(addr as u64, addrlen as u64) } == false {
+                if verify_area(virt_addr, addrlen as usize).is_err() {
                     // 地址空间超出了用户空间的范围,不合法
                     Err(SystemError::EFAULT)
                 } else {
@@ -776,11 +783,13 @@ impl Syscall {
                 let flags = args[3] as u32;
                 let addr = args[4] as *const SockAddr;
                 let addrlen = args[5] as usize;
+                let virt_buf = VirtAddr::new(buf as usize);
+                let virt_addr = VirtAddr::new(addr as usize);
                 // 验证buf的地址是否合法
-                if unsafe { verify_area(buf as u64, len as u64) } == false {
+                if verify_area(virt_buf, len as usize).is_err() {
                     // 地址空间超出了用户空间的范围,不合法
                     Err(SystemError::EFAULT)
-                } else if unsafe { verify_area(addr as u64, addrlen as u64) } == false {
+                } else if verify_area(virt_addr, addrlen as usize).is_err() {
                     // 地址空间超出了用户空间的范围,不合法
                     Err(SystemError::EFAULT)
                 } else {
@@ -795,25 +804,23 @@ impl Syscall {
                 let flags = args[3] as u32;
                 let addr = args[4] as *mut SockAddr;
                 let addrlen = args[5] as *mut usize;
-
+                let virt_buf = VirtAddr::new(buf as usize);
+                let virt_addrlen = VirtAddr::new(addrlen as usize);
+                let virt_addr = VirtAddr::new(addr as usize);
                 let security_check = || {
                     // 验证buf的地址是否合法
-                    if unsafe { verify_area(buf as u64, len as u64) } == false {
+                    if verify_area(virt_buf, len as usize).is_err() {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
                     }
 
                     // 验证addrlen的地址是否合法
-                    if unsafe { verify_area(addrlen as u64, core::mem::size_of::<u32>() as u64) }
-                        == false
-                    {
+                    if verify_area(virt_addrlen, core::mem::size_of::<u32>() as usize).is_err() {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
                     }
 
-                    if unsafe { verify_area(addr as u64, core::mem::size_of::<SockAddr>() as u64) }
-                        == false
-                    {
+                    if verify_area(virt_addr, core::mem::size_of::<SockAddr>() as usize).is_err() {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
                     }
@@ -831,14 +838,14 @@ impl Syscall {
             SYS_RECVMSG => {
                 let msg = args[1] as *mut crate::net::syscall::MsgHdr;
                 let flags = args[2] as u32;
+                let virt_msg = VirtAddr::new(msg as usize);
                 let security_check = || {
                     // 验证msg的地址是否合法
-                    if unsafe {
-                        verify_area(
-                            msg as u64,
-                            core::mem::size_of::<crate::net::syscall::MsgHdr>() as u64,
-                        )
-                    } == false
+                    if verify_area(
+                        virt_msg,
+                        core::mem::size_of::<crate::net::syscall::MsgHdr>() as usize,
+                    )
+                    .is_err()
                     {
                         // 地址空间超出了用户空间的范围,不合法
                         return Err(SystemError::EFAULT);
@@ -867,19 +874,19 @@ impl Syscall {
             SYS_GETTIMEOFDAY => {
                 let timeval = args[0] as *mut PosixTimeval;
                 let timezone_ptr = args[1] as *mut PosixTimeZone;
+                let virt_timeval = VirtAddr::new(timeval as usize);
+                let virt_timezone_ptr = VirtAddr::new(timezone_ptr as usize);
                 let security_check = || {
-                    if unsafe {
-                        verify_area(timeval as u64, core::mem::size_of::<PosixTimeval>() as u64)
-                    } == false
+                    if verify_area(virt_timeval, core::mem::size_of::<PosixTimeval>() as usize)
+                        .is_err()
                     {
                         return Err(SystemError::EFAULT);
                     }
-                    if unsafe {
-                        verify_area(
-                            timezone_ptr as u64,
-                            core::mem::size_of::<PosixTimeZone>() as u64,
-                        )
-                    } == false
+                    if verify_area(
+                        virt_timezone_ptr,
+                        core::mem::size_of::<PosixTimeZone>() as usize,
+                    )
+                    .is_err()
                     {
                         return Err(SystemError::EFAULT);
                     }
@@ -898,7 +905,8 @@ impl Syscall {
             }
             SYS_MMAP => {
                 let len = page_align_up(args[1]);
-                if unsafe { !verify_area(args[0] as u64, len as u64) } {
+                let virt_addr = VirtAddr::new(args[0] as usize);
+                if verify_area(virt_addr, len as usize).is_err() {
                     Err(SystemError::EFAULT)
                 } else {
                     Self::mmap(
@@ -932,6 +940,18 @@ impl Syscall {
                 }
             }
 
+            SYS_FSTAT => {
+                let fd = args[0] as i32;
+                let kstat = args[1] as *mut PosixKstat;
+                let vaddr = VirtAddr::new(kstat as usize);
+                // FIXME 由于c中的verify_area与rust中的verify_area重名,所以在引入时加了前缀区分
+                // TODO 应该将用了c版本的verify_area都改为rust的verify_area
+                match verify_area(vaddr, core::mem::size_of::<PosixKstat>()) {
+                    Ok(_) => Self::fstat(fd, kstat),
+                    Err(e) => Err(e),
+                }
+            }
+
             _ => panic!("Unsupported syscall ID: {}", syscall_num),
         };
 

+ 15 - 12
kernel/src/syscall/syscall_num.h

@@ -40,20 +40,23 @@
 #define SYS_DUP2 29
 #define SYS_SOCKET 30 // 创建一个socket
 
-#define SYS_SETSOCKOPT 31 // 设置socket的选项
-#define SYS_GETSOCKOPT 32 // 获取socket的选项
-#define SYS_CONNECT 33    // 连接到一个socket
-#define SYS_BIND 34       // 绑定一个socket
-#define SYS_SENDTO 35     // 向一个socket发送数据
-#define SYS_RECVFROM 36   // 从一个socket接收数据
-#define SYS_RECVMSG 37    // 从一个socket接收消息
-#define SYS_LISTEN 38     // 监听一个socket
-#define SYS_SHUTDOWN 39   // 关闭socket
-#define SYS_ACCEPT 40     // 接受一个socket连接
+#define SYS_SETSOCKOPT 31  // 设置socket的选项
+#define SYS_GETSOCKOPT 32  // 获取socket的选项
+#define SYS_CONNECT 33     // 连接到一个socket
+#define SYS_BIND 34        // 绑定一个socket
+#define SYS_SENDTO 35      // 向一个socket发送数据
+#define SYS_RECVFROM 36    // 从一个socket接收数据
+#define SYS_RECVMSG 37     // 从一个socket接收消息
+#define SYS_LISTEN 38      // 监听一个socket
+#define SYS_SHUTDOWN 39    // 关闭socket
+#define SYS_ACCEPT 40      // 接受一个socket连接
 
-#define SYS_GETSOCKNAME 41  // 获取socket的名字
-#define SYS_GETPEERNAME 42  // 获取socket的对端名字
+#define SYS_GETSOCKNAME 41   // 获取socket的名字
+#define SYS_GETPEERNAME 42   // 获取socket的对端名字
 #define SYS_GETTIMEOFDAY 43 // 获取当前时间
 #define SYS_MMAP 44         // 内存映射
 #define SYS_MUNMAP 45       // 内存解除映射
 #define SYS_MPROTECT 46     // 内存保护
+
+#define SYS_FSTAT 47        // 根据文件描述符获取文件信息
+

+ 26 - 0
user/apps/test_fstat/Makefile

@@ -0,0 +1,26 @@
+CC=$(DragonOS_GCC)/x86_64-elf-gcc
+LD=ld
+OBJCOPY=objcopy
+# 修改这里,把它改为你的relibc的sysroot路径
+RELIBC_OPT=$(DADK_BUILD_CACHE_DIR_RELIBC_0_1_0)
+CFLAGS=-I $(RELIBC_OPT)/include -D__dragonos__
+
+tmp_output_dir=$(ROOT_PATH)/bin/tmp/user
+output_dir=$(DADK_BUILD_CACHE_DIR_TEST_FSTAT_0_1_0)
+
+LIBC_OBJS:=$(shell find $(RELIBC_OPT)/lib -name "*.o" | sort )
+LIBC_OBJS+=$(RELIBC_OPT)/lib/libc.a
+
+all: main.o
+	mkdir -p $(tmp_output_dir)
+	
+	$(LD) -b elf64-x86-64 -z muldefs -o $(tmp_output_dir)/test_fstat  $(shell find . -name "*.o") $(LIBC_OBJS) -T link.lds
+
+	$(OBJCOPY) -I elf64-x86-64 -R ".eh_frame" -R ".comment" -O elf64-x86-64 $(tmp_output_dir)/test_fstat $(output_dir)/test_fstat.elf
+	
+	mv $(output_dir)/test_fstat.elf $(output_dir)/test_fstat
+main.o: main.c
+	$(CC) $(CFLAGS) -c main.c  -o main.o
+
+clean:
+	rm -f *.o

+ 239 - 0
user/apps/test_fstat/link.lds

@@ -0,0 +1,239 @@
+/* Script for -z combreloc */
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+   Copying and distribution of this script, with or without modification,
+   are permitted in any medium without royalty provided the copyright
+   notice and this notice are preserved.  */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
+              "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x20000000) + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .note.gnu.build-id  : { *(.note.gnu.build-id) }
+  .hash           : { *(.hash) }
+  .gnu.hash       : { *(.gnu.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
+      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
+      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
+      *(.rela.ifunc)
+    }
+  .rela.plt       :
+    {
+      *(.rela.plt)
+      PROVIDE_HIDDEN (__rela_iplt_start = .);
+      *(.rela.iplt)
+      PROVIDE_HIDDEN (__rela_iplt_end = .);
+    }
+  . = ALIGN(CONSTANT (MAXPAGESIZE));
+  .init           :
+  {
+    KEEP (*(SORT_NONE(.init)))
+  }
+  .plt            : { *(.plt) *(.iplt) }
+.plt.got        : { *(.plt.got) }
+.plt.sec        : { *(.plt.sec) }
+  .text           :
+  {
+    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+    *(.text.exit .text.exit.*)
+    *(.text.startup .text.startup.*)
+    *(.text.hot .text.hot.*)
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf.em.  */
+    *(.gnu.warning)
+  }
+  .fini           :
+  {
+    KEEP (*(SORT_NONE(.fini)))
+  }
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  . = ALIGN(CONSTANT (MAXPAGESIZE));
+  /* Adjust the address for the rodata segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)));
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
+  /* These sections are generated by the Sun/Oracle C++ compiler.  */
+  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
+  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
+  /* Thread Local Storage sections  */
+  .tdata          :
+   {
+     PROVIDE_HIDDEN (__tdata_start = .);
+     *(.tdata .tdata.* .gnu.linkonce.td.*)
+   }
+  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .preinit_array    :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  }
+  .init_array    :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  }
+  .fini_array    :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    /* We don't want to include the .ctor section from
+       the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
+  .dynamic        : { *(.dynamic) }
+  .got            : { *(.got) *(.igot) }
+  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
+  .got.plt        : { *(.got.plt) *(.igot.plt) }
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  _edata = .; PROVIDE (edata = .);
+  . = .;
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we do not
+      pad the .data section.  */
+   . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+  .lbss   :
+  {
+    *(.dynlbss)
+    *(.lbss .lbss.* .gnu.linkonce.lb.*)
+    *(LARGE_COMMON)
+  }
+  . = ALIGN(64 / 8);
+  . = SEGMENT_START("ldata-segment", .);
+  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+  }
+  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.ldata .ldata.* .gnu.linkonce.l.*)
+    . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+  . = ALIGN(64 / 8);
+  _end = .; PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* DWARF 3 */
+  .debug_pubtypes 0 : { *(.debug_pubtypes) }
+  .debug_ranges   0 : { *(.debug_ranges) }
+  /* DWARF Extension.  */
+  .debug_macro    0 : { *(.debug_macro) }
+  .debug_addr     0 : { *(.debug_addr) }
+  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
+  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}

+ 35 - 0
user/apps/test_fstat/main.c

@@ -0,0 +1,35 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+int main()
+{
+
+    int fd = open("/bin/about.elf", O_RDONLY);
+    if (fd == -1)
+        return 0;
+    printf("fd = %d\n", fd);
+    struct stat *st = (struct stat *)malloc(sizeof(struct stat));
+    fstat(fd, st);
+    printf("stat size = %d\n", sizeof(struct stat));
+    // FIXME 打印数据时内存出错
+    printf("====================\n");
+    printf("st address: %#018lx\n", st);
+    printf("st_dev = %d\n", (*st).st_dev);
+    printf("st_ino = %d\n", (*st).st_ino);
+    printf("st_mode = %d\n", (*st).st_mode);
+    printf("st_nlink = %d\n", (*st).st_nlink);
+    printf("st_uid = %d\n", (*st).st_uid);
+    printf("st_gid = %d\n", (*st).st_gid);
+    printf("st_rdev = %d\n", (*st).st_rdev);
+    printf("st_size = %d\n", (*st).st_size);
+    printf("st_blksize = %d\n", (*st).st_blksize);
+    printf("st_blocks = %d\n", (*st).st_blocks);
+    printf("st_atim.sec= %d\tst_atim.nsec= %d\n", (*st).st_atim.tv_sec, (*st).st_atim.tv_nsec);
+    printf("st_mtim.sec= %d\tst_mtim.nsec= %d\n", (*st).st_mtim.tv_sec, (*st).st_mtim.tv_nsec);
+    printf("st_ctim.sec= %d\tst_ctim.nsec= %d\n", (*st).st_ctim.tv_sec, (*st).st_ctim.tv_nsec);
+
+    return 0;
+}

+ 33 - 0
user/dadk/config/test_fstat-0.1.0.dadk

@@ -0,0 +1,33 @@
+{
+  "name": "test_fstat",
+  "version": "0.1.0",
+  "description": "一个用来测试fstat能够正常运行的app",
+  "task_type": {
+    "BuildFromSource": {
+      "Local": {
+        "path": "apps/test_fstat"
+      }
+    }
+  },
+  "depends": [
+    {
+      "name": "relibc",
+      "version": "0.1.0"
+    }
+  ],
+  "build": {
+    "build_command": "make"
+  },
+  "install": {
+    "in_dragonos_path": "/bin"
+  },
+  "clean": {
+    "clean_command": "make clean"
+  },
+  "envs": [
+    {
+      "key": "__dragonos__",
+      "value": "__dragonos__"
+    }
+  ]
+}