Forráskód Böngészése

feat(syscall): add statx

greatbridf 1 éve
szülő
commit
2656facbab

+ 4 - 0
gblibc/include/fcntl.h

@@ -11,6 +11,10 @@
 #define O_APPEND (1 << 5)
 #define O_TRUNC (1 << 6)
 
+#define AT_FDCWD (-100)
+
+#define AT_STATX_SYNC_TYPE 0x6000
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 59 - 0
gblibc/include/sys/stat.h

@@ -0,0 +1,59 @@
+#ifndef __GBLIBC_SYS_STAT_H
+#define __GBLIBC_SYS_STAT_H
+
+#include <stdint.h>
+
+#define STATX_TYPE (1 << 0)
+#define STATX_MODE (1 << 1)
+#define STATX_NLINK (1 << 2)
+#define STATX_UID (1 << 3)
+#define STATX_GID (1 << 4)
+#define STATX_ATIME (1 << 5)
+#define STATX_MTIME (1 << 6)
+#define STATX_CTIME (1 << 7)
+#define STATX_INO (1 << 8)
+#define STATX_SIZE (1 << 9)
+#define STATX_BLOCKS (1 << 10)
+#define STATX_BASIC_STATS (0x7ff)
+#define STATX_BTIME (1 << 11)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct statx_timestamp {
+    int64_t tv_sec;
+    uint32_t tv_nsec;
+    int32_t __reserved;
+};
+
+struct statx {
+    uint32_t stx_mask;
+    uint32_t stx_blksize;
+    uint64_t stx_attributes;
+    uint32_t stx_nlink;
+    uint32_t stx_uid;
+    uint32_t stx_gid;
+    uint16_t stx_mode;
+    uint16_t __spare0[1];
+    uint64_t stx_ino;
+    uint64_t stx_size;
+    uint64_t stx_blocks;
+    uint64_t stx_attributes_mask;
+    struct statx_timestamp stx_atime;
+    struct statx_timestamp stx_btime;
+    struct statx_timestamp stx_ctime;
+    struct statx_timestamp stx_mtime;
+    uint32_t stx_rdev_major;
+    uint32_t stx_rdev_minor;
+    uint32_t stx_dev_major;
+    uint32_t stx_dev_minor;
+    uint64_t stx_mnt_id;
+    uint64_t stx_dio_alignment[13];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 38 - 0
gblibstdc++/include/bits/alltypes.h

@@ -0,0 +1,38 @@
+#ifndef __GBLIBC_BITS_ALLTYPES_H
+#define __GBLIBC_BITS_ALLTYPES_H
+
+
+#define S_IFMT 0170000
+
+#define S_IFSOCK 0140000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFBLK 0060000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+
+#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK)
+#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK)
+#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
+#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK)
+#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR)
+#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO)
+
+// gblibc extension
+#define S_ISBLKCHR(m) (S_ISBLK(m) || S_ISCHR(m))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned uid_t;
+typedef unsigned gid_t;
+typedef unsigned mode_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1 - 1
include/fs/fat.hpp

@@ -167,7 +167,7 @@ public:
     ~fat32();
 
     virtual size_t inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override;
-    virtual int inode_stat(dentry* ent, stat* st) override;
+    virtual int inode_stat(dentry* ent, statx* st, unsigned int mask) override;
     virtual int inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& callback) override;
 };
 

+ 10 - 23
include/kernel/vfs.hpp

@@ -5,6 +5,9 @@
 #include <vector>
 #include <functional>
 
+#include <sys/stat.h>
+#include <bits/alltypes.h>
+
 #include <assert.h>
 #include <kernel/event/evtqueue.hpp>
 #include <stdint.h>
@@ -40,22 +43,14 @@ using blkcnt_t = size_t;
 
 class vfs;
 
-union inode_flags {
-    uint32_t v;
-    struct {
-        uint32_t file : 1;
-        uint32_t directory : 1;
-        uint32_t mount_point : 1;
-        uint32_t special_node : 1;
-    } in;
-};
-
 struct inode {
-    inode_flags flags;
-    uint32_t perm;
     ino_t ino;
     vfs* fs;
     size_t size;
+
+    mode_t mode;
+    uid_t uid;
+    gid_t gid;
 };
 
 #define SN_INVALID (0xffffffff)
@@ -83,14 +78,6 @@ struct special_node {
     uint32_t data2;
 };
 
-struct stat {
-    ino_t st_ino;
-    node_t st_rdev;
-    size_t st_size;
-    blksize_t st_blksize;
-    blkcnt_t st_blocks;
-};
-
 struct PACKED user_dirent {
     ino_t d_ino; // inode number
     uint32_t d_off; // ignored
@@ -183,7 +170,7 @@ protected:
     dentry _root;
 
 protected:
-    inode* cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino);
+    inode* cache_inode(size_t size, ino_t ino, mode_t mode, uid_t uid, gid_t gid);
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
 
@@ -209,7 +196,7 @@ public:
     virtual int inode_mknode(dentry* dir, const char* filename, union node_t sn);
     virtual int inode_rmfile(dentry* dir, const char* filename);
     virtual int inode_mkdir(dentry* dir, const char* dirname);
-    virtual int inode_stat(dentry* dir, stat* stat);
+    virtual int inode_stat(dentry* dent, statx* buf, unsigned int mask);
     virtual uint32_t inode_getnode(inode* file);
 
     // parameter 'length' in callback:
@@ -295,7 +282,7 @@ int vfs_mkfile(fs::vfs::dentry* dir, const char* filename);
 int vfs_mknode(fs::vfs::dentry* dir, const char* filename, node_t sn);
 int vfs_rmfile(fs::vfs::dentry* dir, const char* filename);
 int vfs_mkdir(fs::vfs::dentry* dir, const char* dirname);
-int vfs_stat(fs::vfs::dentry* ent, stat* stat);
+int vfs_stat(fs::vfs::dentry* dent, statx* stat, unsigned int mask);
 
 // @param: pwd: current working directory
 //              if nullptr, use root directory

+ 48 - 17
src/fs/fat.cpp

@@ -86,13 +86,12 @@ int fat32::inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_f
             fs::ino_t ino = _rearrange(d);
             auto* ind = get_inode(ino);
             if (!ind) {
-                ind = cache_inode({ .in {
-                                      .file = !d->attributes.subdir,
-                                      .directory = d->attributes.subdir,
-                                      .mount_point = 0,
-                                      .special_node = 0,
-                                  } },
-                    0777, d->size, ino);
+                mode_t mode = 0777;
+                if (d->attributes.subdir)
+                    mode |= S_IFDIR;
+                else
+                    mode |= S_IFREG;
+                ind = cache_inode(d->size, ino, mode, 0, 0);
             }
 
             types::string<> fname;
@@ -114,8 +113,7 @@ int fat32::inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_f
                 else
                     fname += toupper(d->extension[i]);
             }
-            auto ret = filldir(fname.c_str(), 0, ind->ino,
-                (ind->flags.in.directory || ind->flags.in.mount_point) ? DT_DIR : DT_REG);
+            auto ret = filldir(fname.c_str(), 0, ind->ino, ind->mode & S_IFMT);
 
             if (ret != GB_OK) {
                 release_cluster(next);
@@ -175,10 +173,8 @@ fat32::fat32(inode* _device)
     while ((next = fat[next]) < EOC)
         ++_root_dir_clusters;
     auto* n = cache_inode(
-        { INODE_MNT | INODE_DIR },
-        0777,
         _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE,
-        root_dir);
+        root_dir, S_IFDIR | 0777, 0, 0);
     register_root_node(n);
 }
 
@@ -238,12 +234,47 @@ size_t fat32::inode_read(inode* file, char* buf, size_t buf_size, size_t offset,
     return orig_n - n;
 }
 
-int fat32::inode_stat(dentry* ent, stat* st)
+int fat32::inode_stat(dentry* ent, statx* st, unsigned int mask)
 {
-    st->st_size = ent->ind->size;
-    st->st_blksize = 4096;
-    st->st_blocks = (ent->ind->size + 4095) / 4096;
-    st->st_ino = ent->ind->ino;
+    st->stx_mask = 0;
+    if (mask & STATX_SIZE) {
+        st->stx_size = ent->ind->size;
+        st->stx_mask |= STATX_SIZE;
+    }
+    
+    if (mask & STATX_BLOCKS) {
+        st->stx_blocks = align_up<12>(ent->ind->size) / 512;
+        st->stx_blksize = 4096;
+        st->stx_mask |= STATX_BLOCKS;
+    }
+
+    st->stx_mode = 0;
+    if (mask & STATX_MODE) {
+        st->stx_mode |= ent->ind->mode & ~S_IFMT;
+        st->stx_mask |= STATX_MODE;
+    }
+
+    if (mask & STATX_TYPE) {
+        st->stx_mode |= ent->ind->mode & S_IFMT;
+        st->stx_mask |= STATX_TYPE;
+    }
+
+    if (mask & STATX_INO) {
+        st->stx_ino = ent->ind->ino;
+        st->stx_mask |= STATX_INO;
+    }
+
+    if (mask & STATX_UID) {
+        st->stx_uid = ent->ind->uid;
+        st->stx_mask |= STATX_UID;
+    }
+
+    if (mask & STATX_GID) {
+        st->stx_gid = ent->ind->gid;
+        st->stx_mask |= STATX_GID;
+    }
+
     return GB_OK;
 }
+
 } // namespace fs::fat

+ 1 - 1
src/kernel/mem.cpp

@@ -472,7 +472,7 @@ int mmap(
 {
     auto& mms = current_process->mms;
 
-    if (unlikely(!file->flags.in.file && !file->flags.in.special_node)) {
+    if (!S_ISREG(file->mode) && !S_ISBLK(file->mode)) [[unlikely]] {
         errno = EINVAL;
         return GB_FAILED;
     }

+ 1 - 1
src/kernel/process.cpp

@@ -102,7 +102,7 @@ int filearr::open(const process &current, const char *filename, uint32_t flags)
     }
 
     // check whether dentry is a file if O_DIRECTORY is set
-    if ((flags & O_DIRECTORY) && !dentry->ind->flags.in.directory) {
+    if ((flags & O_DIRECTORY) && !S_ISDIR(dentry->ind->mode)) {
         errno = ENOTDIR;
         return -1;
     }

+ 74 - 8
src/kernel/syscall.cpp

@@ -4,6 +4,7 @@
 #include <bits/ioctl.h>
 #include <sys/prctl.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
 #include <time.h>
 #include <kernel/user/thread_local.hpp>
 #include <kernel/errno.h>
@@ -95,7 +96,7 @@ int _syscall_write(interrupt_stack* data)
 
     switch (file->type) {
     case fs::file::types::ind: {
-        if (file->ptr.ind->flags.in.directory)
+        if (S_ISDIR(file->ptr.ind->mode))
             return -EBADF;
 
         int n_wrote = fs::vfs_write(file->ptr.ind, buf, file->cursor, n);
@@ -128,7 +129,7 @@ int _syscall_read(interrupt_stack* data)
 
     switch (file->type) {
     case fs::file::types::ind: {
-        if (file->ptr.ind->flags.in.directory)
+        if (S_ISDIR(file->ptr.ind->mode))
             return -EBADF;
 
         // TODO: copy to user function !IMPORTANT
@@ -168,7 +169,7 @@ int _syscall_chdir(interrupt_stack* data)
     if (!dir)
         return -ENOENT;
 
-    if (!dir->ind->flags.in.directory)
+    if (!S_ISDIR(dir->ind->mode))
         return -ENOTDIR;
 
     current_process->pwd.clear();
@@ -273,7 +274,7 @@ int _syscall_getdents(interrupt_stack* data)
     SYSCALL_ARG3(size_t, cnt);
 
     auto* dir = current_process->files[fd];
-    if (dir->type != fs::file::types::ind || !dir->ptr.ind->flags.in.directory)
+    if (dir->type != fs::file::types::ind || !S_ISDIR(dir->ptr.ind->mode))
         return -ENOTDIR;
 
     size_t orig_cnt = cnt;
@@ -378,7 +379,7 @@ int _syscall_dup2(interrupt_stack* data)
 
 int _syscall_pipe(interrupt_stack* data)
 {
-    SYSCALL_ARG1(int*, pipefd);
+    SYSCALL_ARG1(int* __user, pipefd);
     return current_process->files.pipe(pipefd);
 }
 
@@ -464,7 +465,7 @@ int _syscall_getppid(interrupt_stack*)
 
 int _syscall_set_thread_area(interrupt_stack* data)
 {
-    SYSCALL_ARG1(kernel::user::user_desc*, ptr);
+    SYSCALL_ARG1(kernel::user::user_desc* __user, ptr);
     return kernel::user::set_thread_area(ptr);
 }
 
@@ -479,7 +480,7 @@ int _syscall_set_tid_address(interrupt_stack* data)
 ssize_t _syscall_writev(interrupt_stack* data)
 {
     SYSCALL_ARG1(int, fd);
-    SYSCALL_ARG2(const iovec*, iov);
+    SYSCALL_ARG2(const iovec* __user, iov);
     SYSCALL_ARG3(int, iovcnt);
 
     auto* file = current_process->files[fd];
@@ -489,7 +490,7 @@ ssize_t _syscall_writev(interrupt_stack* data)
 
     switch (file->type) {
     case fs::file::types::ind: {
-        if (file->ptr.ind->flags.in.directory)
+        if (S_ISDIR(file->ptr.ind->mode))
             return -EBADF;
 
         ssize_t n_wrote = 0;
@@ -642,6 +643,69 @@ int _syscall_munmap(interrupt_stack* data)
     return current_process->mms.unmap(addr, len, false);
 }
 
+[[noreturn]] static void not_implemented()
+{
+    console->print("\n[kernel] this function is not implemented\n");
+    kill_current(-1);
+}
+
+int _syscall_sendfile64(interrupt_stack*)
+{
+    not_implemented();
+
+    // SYSCALL_ARG1(int, out_fd);
+    // SYSCALL_ARG2(int, in_fd);
+    // SYSCALL_ARG3(off_t*, offset);
+    // SYSCALL_ARG4(size_t, count);
+
+    // auto* out_file = current_process->files[out_fd];
+    // auto* in_file = current_process->files[in_fd];
+
+    // if (!out_file || !in_file)
+    //     return -EBADF;
+
+    // if (out_file->type != fs::file::types::ind
+    //     || in_file->type != fs::file::types::ind)
+    //     return -EINVAL;
+
+    // if (!out_file->flags.write || !in_file->flags.read)
+    //     return -EBADF;
+
+    // if (out_file->ptr.ind->flags.in.directory
+    //     || in_file->ptr.ind->flags.in.directory)
+    //     return -EBADF;
+
+    // if (offset)
+    //     return -EINVAL;
+}
+
+int _syscall_statx(interrupt_stack* data)
+{
+    SYSCALL_ARG1(int, dirfd);
+    SYSCALL_ARG2(const char* __user, path);
+    SYSCALL_ARG3(int, flags);
+    SYSCALL_ARG4(unsigned int, mask);
+    SYSCALL_ARG5(statx* __user, statxbuf);
+
+    // AT_STATX_SYNC_TYPE is the default value
+    if (flags != 0 && !(flags & AT_STATX_SYNC_TYPE))
+        not_implemented();
+
+    if (dirfd != AT_FDCWD)
+        not_implemented();
+
+    auto* dent = fs::vfs_open(*current_process->root,
+        current_process->pwd.c_str(), path);
+
+    if (!dent)
+        return -ENOENT;
+
+    // TODO: copy to user
+    auto ret = fs::vfs_stat(dent, statxbuf, mask);
+
+    return ret;
+}
+
 extern "C" void syscall_entry(interrupt_stack* data)
 {
     int syscall_no = SYSCALL_NO;
@@ -693,9 +757,11 @@ void init_syscall(void)
     syscall_handlers[0xb7] = _syscall_getcwd;
     syscall_handlers[0xc0] = _syscall_mmap_pgoff;
     syscall_handlers[0xc7] = _syscall_getuid;
+    syscall_handlers[0xef] = _syscall_sendfile64;
     syscall_handlers[0xf3] = _syscall_set_thread_area;
     syscall_handlers[0xfc] = _syscall_exit; // we implement exit_group as exit for now
     syscall_handlers[0x102] = _syscall_set_tid_address;
+    syscall_handlers[0x17f] = _syscall_statx;
     syscall_handlers[0x193] = _syscall_clock_gettime64;
     // syscall_handlers[35] = _syscall_sleep;
 }

+ 65 - 44
src/kernel/vfs.cpp

@@ -3,6 +3,8 @@
 #include <bit>
 #include <utility>
 
+#include <bits/alltypes.h>
+
 #include <assert.h>
 #include <kernel/errno.h>
 #include <kernel/mem.h>
@@ -27,7 +29,7 @@ struct tmpfs_file_entry {
 fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type _name)
     : parent(_parent) , ind(_ind) , flags { } , name(_name)
 {
-    if (!_ind || _ind->flags.in.directory) {
+    if (!_ind || S_ISDIR(ind->mode)) {
         children = types::pnew<allocator_type>(children);
         idx_children = types::pnew<allocator_type>(idx_children);
     }
@@ -51,7 +53,7 @@ fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name, bool set_
 }
 fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
 {
-    if (!ind->flags.in.directory)
+    if (!S_ISDIR(ind->mode))
         return nullptr;
 
     if (name[0] == '.') {
@@ -115,10 +117,11 @@ void fs::vfs::dentry::path(
     }
 }
 
-fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino)
+fs::inode* fs::vfs::cache_inode(size_t size, ino_t ino,
+    mode_t mode, uid_t uid, gid_t gid)
 {
     auto [ iter, inserted ] =
-        _inodes.try_emplace(ino, inode { flags, perm, ino, this, size });
+        _inodes.try_emplace(ino, inode { ino, this, size, mode, uid, gid });
     return &iter->second;
 }
 fs::inode* fs::vfs::get_inode(ino_t ino)
@@ -139,7 +142,7 @@ int fs::vfs::load_dentry(dentry* ent)
 {
     auto* ind = ent->ind;
 
-    if (!ind->flags.in.directory) {
+    if (!S_ISDIR(ind->mode)) {
         errno = ENOTDIR;
         return GB_FAILED;
     }
@@ -164,7 +167,7 @@ int fs::vfs::load_dentry(dentry* ent)
 }
 int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 {
-    if (!mnt->ind->flags.in.directory) {
+    if (!S_ISDIR(mnt->ind->mode)) {
         errno = ENOTDIR;
         return GB_FAILED;
     }
@@ -177,8 +180,6 @@ int fs::vfs::mount(dentry* mnt, vfs* new_fs)
     auto* orig_ent = mnt->replace(new_ent);
     _mount_recover_list.emplace(new_ent, orig_ent);
 
-    new_ent->ind->flags.in.mount_point = 1;
-
     return GB_OK;
 }
 size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
@@ -211,7 +212,7 @@ int fs::vfs::inode_mkdir(dentry*, const char*)
     assert(false);
     return GB_FAILED;
 }
-int fs::vfs::inode_stat(dentry*, stat*)
+int fs::vfs::inode_stat(dentry*, statx*, unsigned int)
 {
     assert(false);
     return GB_FAILED;
@@ -290,7 +291,7 @@ protected:
 
     virtual int inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& filldir) override
     {
-        if (!dir->flags.in.directory) {
+        if (!S_ISDIR(dir->mode)) {
             return -1;
         }
 
@@ -303,13 +304,8 @@ protected:
             const auto& entry = entries[off];
             auto* ind = get_inode(entry.ino);
 
-            auto type = DT_REG;
-            if (ind->flags.in.directory)
-                type = DT_DIR;
-            if (ind->flags.in.special_node)
-                type = DT_BLK;
-
-            auto ret = filldir(entry.filename, 0, entry.ino, type);
+            // inode mode filetype is compatible with user dentry filetype
+            auto ret = filldir(entry.filename, 0, entry.ino, ind->mode & S_IFMT);
             if (ret != GB_OK)
                 break;
         }
@@ -321,7 +317,7 @@ public:
     explicit tmpfs(void)
         : _next_ino(1)
     {
-        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, _savedata(mk_fe_vector()));
+        auto& in = *cache_inode(0, _savedata(mk_fe_vector()), S_IFDIR | 0777, 0, 0);
 
         mklink(&in, &in, ".");
         mklink(&in, &in, "..");
@@ -331,7 +327,7 @@ public:
 
     virtual int inode_mkfile(dentry* dir, const char* filename) override
     {
-        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, _savedata(mk_data_vector()));
+        auto& file = *cache_inode(0, _savedata(mk_data_vector()), S_IFREG | 0777, 0, 0);
         mklink(dir->ind, &file, filename);
         dir->append(get_inode(file.ino), filename, true);
         return GB_OK;
@@ -339,7 +335,7 @@ public:
 
     virtual int inode_mknode(dentry* dir, const char* filename, fs::node_t sn) override
     {
-        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, _savedata(sn.v));
+        auto& node = *cache_inode(0, _savedata(sn.v), S_IFBLK | 0777, 0, 0);
         mklink(dir->ind, &node, filename);
         dir->append(get_inode(node.ino), filename, true);
         return GB_OK;
@@ -347,7 +343,7 @@ public:
 
     virtual int inode_mkdir(dentry* dir, const char* dirname) override
     {
-        auto new_dir = cache_inode({ .v = INODE_DIR }, 0777, 0, _savedata(mk_fe_vector()));
+        auto new_dir = cache_inode(0, _savedata(mk_fe_vector()), S_IFDIR | 0777, 0, 0);
         mklink(new_dir, new_dir, ".");
 
         mklink(dir->ind, new_dir, dirname);
@@ -359,7 +355,7 @@ public:
 
     virtual size_t inode_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
     {
-        if (file->flags.in.file != 1)
+        if (!S_ISREG(file->mode))
             return 0;
 
         auto* data = as_fdata(_getdata(file->ino));
@@ -379,7 +375,7 @@ public:
 
     virtual size_t inode_write(fs::inode* file, const char* buf, size_t offset, size_t n) override
     {
-        if (file->flags.in.file != 1)
+        if (!S_ISREG(file->mode))
             return 0;
 
         auto* data = as_fdata(_getdata(file->ino));
@@ -392,26 +388,51 @@ public:
         return n;
     }
 
-    virtual int inode_stat(dentry* dir, fs::stat* stat) override
+    virtual int inode_stat(dentry* dent, statx* st, unsigned int mask) override
     {
-        auto* file_inode = dir->ind;
-
-        stat->st_ino = file_inode->ino;
-        stat->st_size = file_inode->size;
-        if (file_inode->flags.in.file) {
-            stat->st_rdev.v = 0;
-            stat->st_blksize = 1;
-            stat->st_blocks = file_inode->size;
+        auto* ind = dent->ind;
+        const mode_t mode = ind->mode;
+
+        if (mask & STATX_SIZE) {
+            st->stx_size = ind->size;
+            st->stx_mask |= STATX_SIZE;
+        }
+
+        st->stx_mode = 0;
+        if (mask & STATX_MODE) {
+            st->stx_mode |= ind->mode & ~S_IFMT;
+            st->stx_mask |= STATX_MODE;
+        }
+
+        if (mask & STATX_TYPE) {
+            st->stx_mode |= ind->mode & S_IFMT;
+            if (S_ISBLK(mode)) {
+                fs::node_t nd { (uint32_t)as_val(_getdata(ind->ino)) };
+                st->stx_rdev_major = nd.in.major;
+                st->stx_rdev_minor = nd.in.minor;
+            }
+            st->stx_mask |= STATX_TYPE;
+        }
+
+        if (mask & STATX_INO) {
+            st->stx_ino = ind->ino;
+            st->stx_mask |= STATX_INO;
         }
-        if (file_inode->flags.in.directory) {
-            stat->st_rdev.v = 0;
-            stat->st_blksize = sizeof(fe_t);
-            stat->st_blocks = file_inode->size;
+        
+        if (mask & STATX_BLOCKS) {
+            st->stx_blocks = align_up<9>(ind->size) / 512;
+            st->stx_blksize = 4096;
+            st->stx_mask |= STATX_BLOCKS;
         }
-        if (file_inode->flags.in.special_node) {
-            stat->st_rdev.v = as_val(_getdata(file_inode->ino));
-            stat->st_blksize = 0;
-            stat->st_blocks = 0;
+
+        if (mask & STATX_UID) {
+            st->stx_uid = ind->uid;
+            st->stx_mask |= STATX_UID;
+        }
+
+        if (mask & STATX_GID) {
+            st->stx_gid = ind->gid;
+            st->stx_mask |= STATX_GID;
         }
 
         return GB_OK;
@@ -428,7 +449,7 @@ static fs::special_node sns[8][8];
 
 size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
-    if (file->flags.in.special_node) {
+    if (S_ISBLK(file->mode)) {
         uint32_t ret = file->fs->inode_getnode(file);
         if (ret == SN_INVALID) {
             errno = EINVAL;
@@ -451,7 +472,7 @@ size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset,
 }
 size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
 {
-    if (file->flags.in.special_node) {
+    if (S_ISBLK(file->mode)) {
         uint32_t ret = file->fs->inode_getnode(file);
         if (ret == SN_INVALID) {
             errno = EINVAL;
@@ -539,9 +560,9 @@ fs::vfs::dentry* fs::vfs_open(
     }
 }
 
-int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
+int fs::vfs_stat(fs::vfs::dentry* ent, statx* stat, unsigned int mask)
 {
-    return ent->ind->fs->inode_stat(ent, stat);
+    return ent->ind->fs->inode_stat(ent, stat, mask);
 }
 
 static std::list<fs::vfs*>* fs_es;