Jelajahi Sumber

feat(syscall): impl. symlink and readlink

we can now deploy busybox to /bin

make types::path better

update init script
greatbridf 11 bulan lalu
induk
melakukan
87949fe12a

+ 1 - 0
CMakeLists.txt

@@ -43,6 +43,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/process.cpp
                         src/kernel/tty.cpp
                         src/kernel/syscall.cpp
+                        src/kernel/syscall/fileops.cc
                         src/kernel/mem.cpp
                         src/kernel/module.cc
                         src/kernel/vfs.cpp

+ 7 - 0
include/kernel/syscall.hpp

@@ -3,6 +3,13 @@
 #include <kernel/interrupt.h>
 #include <types/types.h>
 
+#define SYSCALL_ARG1(type, name) type name = (type)((data)->s_regs.ebx)
+#define SYSCALL_ARG2(type, name) type name = (type)((data)->s_regs.ecx)
+#define SYSCALL_ARG3(type, name) type name = (type)((data)->s_regs.edx)
+#define SYSCALL_ARG4(type, name) type name = (type)((data)->s_regs.esi)
+#define SYSCALL_ARG5(type, name) type name = (type)((data)->s_regs.edi)
+#define SYSCALL_ARG6(type, name) type name = (type)((data)->s_regs.ebp)
+
 // return value is stored in %eax and %edx
 typedef int (*syscall_handler)(interrupt_stack* data);
 

+ 1 - 1
include/kernel/vfs.hpp

@@ -103,7 +103,7 @@ int vfs_truncate(inode* file, size_t size);
  * @return A pointer to the opened file or directory entry if found.
  *         Otherwise, nullptr is returned.
  */
-dentry* vfs_open(dentry& root, const types::path& path);
+dentry* vfs_open(dentry& root, const types::path& path, bool follow_symlinks = true, int recurs_no = 0);
 
 } // namespace fs
 

+ 3 - 0
include/kernel/vfs/vfs.hpp

@@ -61,6 +61,8 @@ public:
     virtual int inode_rmfile(dentry* dir, const char* filename);
     virtual int inode_mkdir(dentry* dir, const char* dirname, mode_t mode);
 
+    virtual int symlink(dentry* dir, const char* linkname, const char* target);
+
     // metadata operation
 
     virtual int inode_statx(dentry* dent, statx* buf, unsigned int mask);
@@ -71,6 +73,7 @@ public:
     virtual size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
     virtual size_t write(inode* file, const char* buf, size_t offset, size_t n);
     virtual int dev_id(inode* file, dev_t& out_dev);
+    virtual int readlink(inode* file, char* buf, size_t buf_size);
     virtual int truncate(inode* file, size_t size);
 
     // parameter 'length' in callback:

+ 18 - 17
include/types/path.hpp

@@ -21,7 +21,7 @@ public:
     constexpr path() = default;
     constexpr path(const path& val) = default;
     constexpr path(path&& val) = default;
-    constexpr path(const char* str, size_type len = -1U)
+    explicit constexpr path(const char* str, size_type len = -1U)
     { append(str, len); }
 
     constexpr path& operator=(const path& val) = default;
@@ -55,6 +55,10 @@ public:
     constexpr path& append(const char* str, size_type len = -1U)
     {
         const char* start = str;
+
+        if (len && *start == '/')
+            clear();
+
         while (len-- && *str) {
             if (*str == '/') {
                 if (m_vec.empty() || str != start)
@@ -70,6 +74,14 @@ public:
     }
     constexpr path& append(const path& val)
     {
+        if (&val == this)
+            return *this;
+
+        if (val.is_absolute()) {
+            *this = val;
+            return *this;
+        }
+
         m_vec.insert(m_vec.end(), val.m_vec.begin(), val.m_vec.end());
         return *this;
     }
@@ -86,6 +98,11 @@ public:
     constexpr path& operator+=(const path& val)
     { return append(val); }
 
+    constexpr path operator+(const char* str) const
+    { return path{*this}.append(str); }
+    constexpr path operator+(const path& val)
+    { return path{*this}.append(val); }
+
     constexpr bool operator==(const char* str) const
     {
         return full_path() == str;
@@ -95,20 +112,4 @@ public:
     constexpr iterator end() const { return m_vec.cend(); }
 };
 
-constexpr path make_path(const char* pathstr, const char* pwd)
-{
-    if (*pathstr && pathstr[0] == '/')
-        return pathstr;
-    else
-        return path { pwd }.append(pathstr);
-}
-
-constexpr path make_path(const char* pathstr, const path& pwd)
-{
-    if (*pathstr && pathstr[0] == '/')
-        return pathstr;
-    else
-        return path{pwd}.append(pathstr);
-}
-
 } // namespace types

+ 23 - 20
init_script.sh

@@ -1,18 +1,24 @@
 #!/mnt/busybox sh
 
 BUSYBOX=/mnt/busybox
-alias mkdir="$BUSYBOX mkdir "
-alias mknod="$BUSYBOX mknod "
-alias cat="$BUSYBOX cat "
 
-mkdir -p /etc
-mkdir -p /root
-mkdir -p /dev
+$BUSYBOX mkdir -p /etc
+$BUSYBOX mkdir -p /root
+$BUSYBOX mkdir -p /dev
 
-mknod -m 666 /dev/console c 2 0
-mknod -m 666 /dev/null c 1 0
-mknod -m 666 /dev/sda b 8 0
-mknod -m 666 /dev/sda1 b 8 1
+$BUSYBOX mknod -m 666 /dev/console c 2 0
+$BUSYBOX mknod -m 666 /dev/null c 1 0
+$BUSYBOX mknod -m 666 /dev/sda b 8 0
+$BUSYBOX mknod -m 666 /dev/sda1 b 8 1
+
+echo -n -e "deploying busybox... " > /dev/console
+
+$BUSYBOX mkdir -p /bin
+$BUSYBOX --install -s /bin
+
+export PATH="/bin"
+
+echo ok > /dev/console
 
 cat > /etc/passwd <<EOF
 root:x:0:0:root:/root:/mnt/busybox sh
@@ -22,19 +28,16 @@ cat > /etc/group <<EOF
 root:x:0:root
 EOF
 
+cat > /etc/profile <<EOF
+export PATH=/bin
+EOF
+
 cat > /root/.profile <<EOF
-export PATH=/mnt
 export HOME=/root
 
-export BUSYBOX=/mnt/busybox
-
-alias ls="$BUSYBOX ls "
-alias ll="$BUSYBOX ls -l "
-alias la="$BUSYBOX ls -la "
-
-alias cat="$BUSYBOX cat "
-alias clear="$BUSYBOX clear "
+alias ll="ls -l "
+alias la="ls -la "
 EOF
 
-exec /mnt/init /mnt/busybox sh -l \
+exec /mnt/init /bin/sh -l \
     < /dev/console > /dev/console 2> /dev/console

+ 3 - 3
src/kernel/process.cpp

@@ -439,12 +439,12 @@ void NORETURN _kernel_init(void)
         int ret = fs::create_fs("fat32", fs::make_device(8, 1), new_fs);
         assert(ret == 0);
 
-        auto* mount_point = fs::vfs_open(*fs::fs_root, "/mnt");
+        auto* mount_point = fs::vfs_open(*fs::fs_root, types::path{"/mnt"});
         if (!mount_point) {
             int ret = fs::vfs_mkdir(fs::fs_root, "mnt", 0755);
             assert(ret == GB_OK);
 
-            mount_point = fs::vfs_open(*fs::fs_root, "/mnt");
+            mount_point = fs::vfs_open(*fs::fs_root, types::path{"/mnt"});
         }
 
         assert(mount_point);
@@ -464,7 +464,7 @@ void NORETURN _kernel_init(void)
     d.envp = envp;
     d.system = false;
 
-    d.exec_dent = fs::vfs_open(*fs::fs_root, argv[0]);
+    d.exec_dent = fs::vfs_open(*fs::fs_root, types::path{argv[0]});
     if (!d.exec_dent) {
         console->print("kernel panic: init not found!\n");
         freeze();

+ 18 - 19
src/kernel/syscall.cpp

@@ -3,6 +3,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <poll.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -45,13 +46,6 @@
 #define SYSCALL_NO ((data)->s_regs.eax)
 #define SYSCALL_RETVAL ((data)->s_regs.eax)
 
-#define SYSCALL_ARG1(type, name) type name = (type)((data)->s_regs.ebx)
-#define SYSCALL_ARG2(type, name) type name = (type)((data)->s_regs.ecx)
-#define SYSCALL_ARG3(type, name) type name = (type)((data)->s_regs.edx)
-#define SYSCALL_ARG4(type, name) type name = (type)((data)->s_regs.esi)
-#define SYSCALL_ARG5(type, name) type name = (type)((data)->s_regs.edi)
-#define SYSCALL_ARG6(type, name) type name = (type)((data)->s_regs.ebp)
-
 #define SYSCALL_HANDLERS_SIZE (404)
 syscall_handler syscall_handlers[SYSCALL_HANDLERS_SIZE];
 
@@ -154,7 +148,7 @@ int _syscall_chdir(interrupt_stack* data)
     SYSCALL_ARG1(const char*, path);
 
     auto* dir = fs::vfs_open(*current_process->root,
-        types::make_path(path, current_process->pwd));
+            current_process->pwd + path);
     if (!dir)
         return -ENOENT;
 
@@ -183,8 +177,8 @@ int _syscall_execve(interrupt_stack* data)
     d.system = false;
 
     d.exec_dent = fs::vfs_open(*current_process->root,
-        types::make_path(exec, current_process->pwd));
-    
+            current_process->pwd + exec);
+
     if (!d.exec_dent)
         return -ENOENT;
 
@@ -307,7 +301,7 @@ int _syscall_open(interrupt_stack* data)
     mode &= ~current_process->umask;
 
     return current_process->files.open(*current_process,
-        types::make_path(path, current_process->pwd), flags, mode);
+        current_process->pwd + path, flags, mode);
 }
 
 int _syscall_getcwd(interrupt_stack* data)
@@ -713,7 +707,7 @@ int _syscall_statx(interrupt_stack* data)
     SYSCALL_ARG5(statx* __user, statxbuf);
 
     // AT_STATX_SYNC_AS_STAT is the default value
-    if (flags != AT_STATX_SYNC_AS_STAT && !(flags & AT_SYMLINK_NOFOLLOW)) {
+    if ((flags & AT_STATX_SYNC_TYPE) != AT_STATX_SYNC_AS_STAT) {
         NOT_IMPLEMENTED;
         return -EINVAL;
     }
@@ -724,7 +718,8 @@ int _syscall_statx(interrupt_stack* data)
     }
 
     auto* dent = fs::vfs_open(*current_process->root,
-        types::make_path(path, current_process->pwd));
+            current_process->pwd + path,
+            !(flags & AT_SYMLINK_NOFOLLOW));
 
     if (!dent)
         return -ENOENT;
@@ -957,7 +952,7 @@ int _syscall_mkdir(interrupt_stack* data)
 
     mode &= (~current_process->umask & 0777);
 
-    auto path = types::make_path(pathname, current_process->pwd);
+    auto path = current_process->pwd + pathname;
 
     auto* dent = fs::vfs_open(*current_process->root, path);
     if (dent)
@@ -987,7 +982,7 @@ int _syscall_truncate(interrupt_stack* data)
     SYSCALL_ARG1(const char* __user, pathname);
     SYSCALL_ARG2(long, length);
 
-    auto path = types::make_path(pathname, current_process->pwd);
+    auto path = current_process->pwd + pathname;
 
     auto* dent = fs::vfs_open(*current_process->root, path);
     if (!dent)
@@ -1008,8 +1003,8 @@ int _syscall_unlink(interrupt_stack* data)
 {
     SYSCALL_ARG1(const char* __user, pathname);
 
-    auto path = types::make_path(pathname, current_process->pwd);
-    auto* dent = fs::vfs_open(*current_process->root, path);
+    auto path = current_process->pwd + pathname;
+    auto* dent = fs::vfs_open(*current_process->root, path, false);
 
     if (!dent)
         return -ENOENT;
@@ -1025,7 +1020,7 @@ int _syscall_access(interrupt_stack* data)
     SYSCALL_ARG1(const char* __user, pathname);
     SYSCALL_ARG2(int, mode);
 
-    auto path = types::make_path(pathname, current_process->pwd);
+    auto path = current_process->pwd + pathname;
     auto* dent = fs::vfs_open(*current_process->root, path);
 
     if (!dent)
@@ -1050,7 +1045,7 @@ int _syscall_mknod(interrupt_stack* data)
     SYSCALL_ARG2(mode_t, mode);
     SYSCALL_ARG3(dev_t, dev);
 
-    auto path = types::make_path(pathname, current_process->pwd);
+    auto path = current_process->pwd + pathname;
     auto* dent = fs::vfs_open(*current_process->root, path);
 
     if (dent)
@@ -1188,6 +1183,10 @@ void init_syscall(void)
     syscall_handlers[0x40] = _syscall_getppid;
     syscall_handlers[0x42] = _syscall_setsid;
     syscall_handlers[0x4e] = _syscall_gettimeofday;
+    extern int _syscall_symlink(interrupt_stack*);
+    syscall_handlers[0x53] = _syscall_symlink;
+    extern int _syscall_readlink(interrupt_stack*);
+    syscall_handlers[0x55] = _syscall_readlink;
     syscall_handlers[0x5b] = _syscall_munmap;
     syscall_handlers[0x5c] = _syscall_truncate;
     syscall_handlers[0x72] = _syscall_wait4;

+ 49 - 0
src/kernel/syscall/fileops.cc

@@ -0,0 +1,49 @@
+#include <errno.h>
+
+#include <types/path.hpp>
+
+#include <kernel/process.hpp>
+#include <kernel/syscall.hpp>
+#include <kernel/vfs.hpp>
+
+int _syscall_symlink(interrupt_stack* data)
+{
+    SYSCALL_ARG1(const char __user*, target);
+    SYSCALL_ARG2(const char __user*, linkpath);
+
+    // TODO: use copy_from_user
+    auto path = current_process->pwd + linkpath;
+    auto* dent = fs::vfs_open(*current_process->root, path);
+
+    if (dent)
+        return -EEXIST;
+
+    auto linkname = path.last_name();
+    path.remove_last();
+
+    dent = fs::vfs_open(*current_process->root, path);
+    if (!dent)
+        return -ENOENT;
+
+    return dent->ind->fs->symlink(dent, linkname.c_str(), target);
+}
+
+int _syscall_readlink(interrupt_stack* data)
+{
+    SYSCALL_ARG1(const char __user*, pathname);
+    SYSCALL_ARG2(char __user*, buf);
+    SYSCALL_ARG3(size_t, buf_size);
+
+    // TODO: use copy_from_user
+    auto path = current_process->pwd + pathname;
+    auto* dent = fs::vfs_open(*current_process->root, path, false);
+
+    if (!dent)
+        return -ENOENT;
+
+    if (buf_size <= 0 || !S_ISLNK(dent->ind->mode))
+        return -EINVAL;
+
+    // TODO: use copy_to_user
+    return dent->ind->fs->readlink(dent->ind, buf, buf_size);
+}

+ 51 - 1
src/kernel/vfs.cpp

@@ -224,6 +224,11 @@ int vfs::inode_mkdir(dentry*, const char*, mode_t)
     return -EINVAL;
 }
 
+int vfs::symlink(dentry*, const char*, const char*)
+{
+    return -EINVAL;
+}
+
 int vfs::inode_statx(dentry*, statx*, unsigned int)
 {
     return -EINVAL;
@@ -239,6 +244,11 @@ int vfs::dev_id(inode*, dev_t&)
     return -EINVAL;
 }
 
+int vfs::readlink(inode*, char*, size_t)
+{
+    return -EINVAL;
+}
+
 int vfs::truncate(inode*, size_t)
 {
     return -EINVAL;
@@ -488,16 +498,56 @@ int fs::vfs_mkdir(dentry* dir, const char* dirname, mode_t mode)
     return dir->ind->fs->inode_mkdir(dir, dirname, mode);
 }
 
-dentry* fs::vfs_open(dentry& root, const types::path& path)
+dentry* fs::vfs_open(dentry& root, const types::path& path, bool follow, int recurs_no)
 {
+    // too many recursive search layers will cause stack overflow
+    // so we use 16 for now
+    if (recurs_no >= 16)
+        return nullptr;
+
     dentry* cur = &root;
 
+    types::path curpath("/");
     for (const auto& item : path) {
+        if (S_ISLNK(cur->ind->mode)) {
+            char linkpath[256];
+            int ret = cur->ind->fs->readlink(cur->ind, linkpath, sizeof(linkpath));
+
+            // TODO: return error code
+            if (ret < 0)
+                return nullptr;
+
+            curpath.remove_last();
+            curpath.append(linkpath, ret);
+            cur = fs::vfs_open(root, curpath, true, recurs_no+1);
+
+            if (!cur)
+                return nullptr;
+        }
+
         if (item.empty())
             continue;
         cur = cur->find(item);
         if (!cur)
             return nullptr;
+
+        curpath.append(item.c_str());
+    }
+
+    if (follow && S_ISLNK(cur->ind->mode)) {
+        char linkpath[256];
+        int ret = cur->ind->fs->readlink(cur->ind, linkpath, sizeof(linkpath));
+
+        // TODO: return error code
+        if (ret < 0)
+            return nullptr;
+
+        curpath.remove_last();
+        curpath.append(linkpath, ret);
+        cur = fs::vfs_open(root, curpath, true, recurs_no+1);
+
+        if (!cur)
+            return nullptr;
     }
 
     return cur;

+ 34 - 0
src/kernel/vfs/tmpfs.cc

@@ -2,6 +2,7 @@
 #include <kernel/mm.hpp>
 #include <kernel/log.hpp>
 
+#include <algorithm>
 #include <vector>
 #include <map>
 
@@ -194,6 +195,39 @@ public:
         return GB_OK;
     }
 
+    virtual int symlink(dentry* dir, const char* linkname, const char* target) override
+    {
+        if (!dir->flags.dir)
+            return -ENOTDIR;
+
+        auto* data = mk_data_vector();
+        data->resize(strlen(target));
+        memcpy(data->data(), target, data->size());
+
+        auto& file = *cache_inode(data->size(), _savedata(data), S_IFLNK | 0777, 0, 0);
+        mklink(dir->ind, &file, linkname);
+
+        if (dir->flags.present)
+            dir->append(get_inode(file.ino), linkname);
+
+        return 0;
+    }
+
+    virtual int readlink(inode* file, char* buf, size_t buf_size) override
+    {
+        if (!S_ISLNK(file->mode))
+            return -EINVAL;
+
+        auto* data = as_fdata(_getdata(file->ino));
+        size_t size = data->size();
+
+        size = std::min(size, buf_size);
+
+        memcpy(buf, data->data(), size);
+
+        return size;
+    }
+
     virtual int inode_statx(dentry* dent, statx* st, unsigned int mask) override
     {
         auto* ind = dent->ind;