Jelajahi Sumber

feat(vfs): support opening relative path

greatbridf 1 tahun lalu
induk
melakukan
423968a920

+ 8 - 39
include/kernel/process.hpp

@@ -232,39 +232,7 @@ public:
         return 0;
     }
 
-    // TODO: file opening permissions check
-    int open(const char* filename, uint32_t flags)
-    {
-        auto* dentry = fs::vfs_open(filename);
-
-        if (!dentry) {
-            errno = ENOTFOUND;
-            return -1;
-        }
-
-        // check whether dentry is a file if O_DIRECTORY is set
-        if ((flags & O_DIRECTORY) && !dentry->ind->flags.in.directory) {
-            errno = ENOTDIR;
-            return -1;
-        }
-
-        auto iter = files->emplace(files->cend(), fs::file {
-            fs::file::types::ind,
-            { .ind = dentry->ind },
-            dentry->parent,
-            0,
-            1,
-            {
-                .read = !!(flags & (O_RDONLY | O_RDWR)),
-                .write = !!(flags & (O_WRONLY | O_RDWR)),
-            },
-        });
-
-        int fd = next_fd();
-        auto [ _, inserted ] = arr.emplace(fd, iter);
-        assert(inserted);
-        return fd;
-    }
+    int open(const process& current, const char* filename, uint32_t flags);
 
     constexpr void close(int fd)
     {
@@ -304,17 +272,18 @@ public:
     std::set<kernel::tasks::thread> thds;
     kernel::cond_var cv_wait;
     std::list<wait_obj> waitlist;
-    process_attr attr;
+    process_attr attr {};
     filearr files;
     types::string<> pwd;
     kernel::signal_list signals;
 
-    pid_t pid;
-    pid_t ppid;
-    pid_t pgid;
-    pid_t sid;
+    pid_t pid {};
+    pid_t ppid {};
+    pid_t pgid {};
+    pid_t sid {};
 
-    tty* control_tty;
+    tty* control_tty {};
+    fs::vfs::dentry* root { fs::fs_root };
     std::set<pid_t> children;
 
 public:

+ 9 - 2
include/kernel/vfs.hpp

@@ -162,6 +162,9 @@ public:
 
         dentry* replace(dentry* val);
 
+        // out_dst SHOULD be empty
+        void path(const dentry& root, name_type& out_dst) const;
+
         void invalidate(void);
     };
 
@@ -292,11 +295,15 @@ 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(const char* filename, stat* stat);
 int vfs_stat(fs::vfs::dentry* ent, stat* stat);
 
+// @param: pwd: current working directory
+//              if nullptr, use root directory
+//              if present, must be a valid absolute path
+//
 // @return pointer to the dentry if found, nullptr if not
-fs::vfs::dentry* vfs_open(const char* path);
+fs::vfs::dentry* vfs_open(fs::vfs::dentry& root,
+    const char* pwd, const char* path);
 
 } // namespace fs
 

+ 1 - 1
include/types/elf.hpp

@@ -136,7 +136,7 @@ struct PACKED elf32_section_header_entry {
 };
 
 struct elf32_load_data {
-    const char* exec;
+    const fs::vfs::dentry* exec_dent;
     const char* const* argv;
     const char* const* envp;
     int errcode;

+ 5 - 0
include/types/string.hpp

@@ -63,6 +63,11 @@ public:
     {
         return strcmp(c_str(), rhs.c_str()) == 0;
     }
+    constexpr string& assign(const char* str, size_type n = npos)
+    {
+        this->clear();
+        return this->append(str, n);
+    }
     constexpr string substr(size_type pos, size_type n = npos)
     {
         return string(this->m_arr + pos, n);

+ 3 - 2
src/kernel/hw/ata.cpp

@@ -181,7 +181,8 @@ static inline void mbr_part_probe(fs::inode* drive, uint16_t major, uint16_t min
 {
     struct mbr hda_mbr {
     };
-    auto* dev = fs::vfs_open("/dev");
+    // TODO: devtmpfs
+    auto* dev = fs::vfs_open(*fs::fs_root, nullptr, "/dev");
     assert(dev);
 
     fs::vfs_read(drive, (char*)&hda_mbr, 512, 0, 512);
@@ -228,7 +229,7 @@ void hw::init_ata(void)
         0,
         0xffffffff);
 
-    auto* hda = fs::vfs_open("/dev/hda");
+    auto* hda = fs::vfs_open(*fs::fs_root, nullptr, "/dev/hda");
     assert(hda);
     mbr_part_probe(hda->ind, 2, 1);
 }

+ 48 - 14
src/kernel/process.cpp

@@ -91,12 +91,46 @@ void kernel::tasks::thread::free_kstack(uint32_t p)
     pkstack_bmp->clear(p);
 }
 
+// TODO: file opening permissions check
+int filearr::open(const process &current, const char *filename, uint32_t flags)
+{
+    auto* dentry = fs::vfs_open(*current.root, current.pwd.c_str(), filename);
+
+    if (!dentry) {
+        errno = ENOTFOUND;
+        return -1;
+    }
+
+    // check whether dentry is a file if O_DIRECTORY is set
+    if ((flags & O_DIRECTORY) && !dentry->ind->flags.in.directory) {
+        errno = ENOTDIR;
+        return -1;
+    }
+
+    auto iter = files->emplace(files->cend(), fs::file {
+        fs::file::types::ind,
+        { .ind = dentry->ind },
+        dentry->parent,
+        0,
+        1,
+        {
+            .read = !!(flags & (O_RDONLY | O_RDWR)),
+            .write = !!(flags & (O_WRONLY | O_RDWR)),
+        },
+    });
+
+    int fd = next_fd();
+    auto [ _, inserted ] = arr.emplace(fd, iter);
+    assert(inserted);
+    return fd;
+}
+
 process::process(const process& parent, pid_t pid)
     : mms { *kernel_mms }
     , attr { parent.attr } , pwd { parent.pwd }
     , signals { parent.signals } , pid { pid }
     , ppid { parent.pid } , pgid { parent.pgid } , sid { parent.sid }
-    , control_tty { parent.control_tty } , children {}
+    , control_tty { parent.control_tty }, root { parent.root }
 {
     for (auto& area : parent.mms) {
         if (area.is_kernel_space() || area.attr.in.system)
@@ -109,13 +143,8 @@ process::process(const process& parent, pid_t pid)
 }
 
 process::process(pid_t pid, pid_t ppid)
-    : mms(*kernel_mms)
-    , attr { .system = true }
-    , pwd { "/" }
-    , pid { pid }
-    , ppid { ppid }
-    , pgid {} , sid {} , control_tty {}
-    , children {} { }
+    : mms(*kernel_mms) , attr { .system = true }
+    , pwd { "/" } , pid { pid } , ppid { ppid } { }
 
 void proclist::kill(pid_t pid, int exit_code)
 {
@@ -245,10 +274,10 @@ void NORETURN _kernel_init(void)
     hw::init_ata();
 
     // TODO: parse kernel parameters
-    auto* drive = fs::vfs_open("/dev/hda1");
+    auto* drive = fs::vfs_open(*fs::fs_root, nullptr, "/dev/hda1");
     assert(drive);
     auto* _new_fs = fs::register_fs(new fs::fat::fat32(drive->ind));
-    auto* mnt = fs::vfs_open("/mnt");
+    auto* mnt = fs::vfs_open(*fs::fs_root, nullptr, "/mnt");
     assert(mnt);
     int ret = fs::fs_root->ind->fs->mount(mnt, _new_fs);
     assert(ret == GB_OK);
@@ -260,11 +289,16 @@ void NORETURN _kernel_init(void)
     const char* envp[] = { nullptr };
 
     types::elf::elf32_load_data d;
-    d.exec = "/mnt/init";
     d.argv = argv;
     d.envp = envp;
     d.system = false;
 
+    d.exec_dent = fs::vfs_open(*fs::fs_root, nullptr, "/mnt/init");
+    if (!d.exec_dent) {
+        console->print("kernel panic: init not found!\n");
+        freeze();
+    }
+
     ret = types::elf::elf32_load(&d);
     assert(ret == GB_OK);
 
@@ -330,9 +364,9 @@ void NORETURN init_scheduler(void)
     assert(inserted);
     auto& thd = *iter_thd;
 
-    init.files.open("/dev/console", O_RDONLY);
-    init.files.open("/dev/console", O_WRONLY);
-    init.files.open("/dev/console", O_WRONLY);
+    init.files.open(init, "/dev/console", O_RDONLY);
+    init.files.open(init, "/dev/console", O_WRONLY);
+    init.files.open(init, "/dev/console", O_WRONLY);
 
     // we need interrupts enabled for cow mapping so now we disable it
     // in case timer interrupt mess things up

+ 12 - 4
src/kernel/syscall.cpp

@@ -147,14 +147,16 @@ int _syscall_sleep(interrupt_stack*)
 int _syscall_chdir(interrupt_stack* data)
 {
     const char* path = reinterpret_cast<const char*>(data->s_regs.edi);
-    auto* dir = fs::vfs_open(path);
+    auto* dir = fs::vfs_open(*current_process->root,
+        current_process->pwd.c_str(), path);
     if (!dir)
         return -ENOENT;
 
     if (!dir->ind->flags.in.directory)
         return -ENOTDIR;
 
-    current_process->pwd = path;
+    current_process->pwd.clear();
+    dir->path(*current_process->root, current_process->pwd);
 
     return 0;
 }
@@ -172,9 +174,14 @@ int _syscall_execve(interrupt_stack* data)
     types::elf::elf32_load_data d;
     d.argv = argv;
     d.envp = envp;
-    d.exec = exec;
     d.system = false;
 
+    d.exec_dent = fs::vfs_open(*current_process->root,
+        current_process->pwd.c_str(), exec);
+    
+    if (!d.exec_dent)
+        return -ENOENT;
+
     int ret = types::elf::elf32_load(&d);
     if (ret != GB_OK)
         return -d.errcode;
@@ -281,7 +288,8 @@ int _syscall_open(interrupt_stack* data)
 {
     auto* path = (const char*)data->s_regs.edi;
     uint32_t flags = data->s_regs.esi;
-    return current_process->files.open(path, flags);
+    return current_process->files.open(
+        *current_process, path, flags);
 }
 
 int _syscall_getcwd(interrupt_stack* data)

+ 77 - 55
src/kernel/vfs.cpp

@@ -54,7 +54,14 @@ fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
     if (!ind->flags.in.directory)
         return nullptr;
 
-    if (ind->flags.in.directory && !flags.in.present)
+    if (name[0] == '.') {
+        if (!name[1])
+            return this;
+        if (name[1] == '.' && !name[2])
+            return parent ? parent : this;
+    }
+
+    if (!flags.in.present)
         ind->fs->load_dentry(this);
 
     auto iter = idx_children->find(name);
@@ -80,9 +87,34 @@ void fs::vfs::dentry::invalidate(void)
     flags.in.present = 0;
 }
 fs::vfs::vfs(void)
-    : _root(nullptr, nullptr, "/")
+    : _root(nullptr, nullptr, "")
 {
 }
+
+void fs::vfs::dentry::path(
+    const dentry& root, name_type &out_dst) const
+{
+    const dentry* dents[32];
+    int cnt = 0;
+
+    const dentry* cur = this;
+    while (cur != &root) {
+        assert(cnt < 32);
+        dents[cnt++] = cur;
+        cur = cur->parent;
+    }
+
+    if (cnt == 0) {
+        out_dst += '/';
+        return;
+    }
+
+    for (int i = cnt - 1; i >= 0; --i) {
+        out_dst += '/';
+        out_dst += dents[i]->name;
+    }
+}
+
 fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino)
 {
     auto [ iter, inserted ] =
@@ -457,57 +489,56 @@ int fs::vfs_mkdir(fs::vfs::dentry* dir, const char* dirname)
     return dir->ind->fs->inode_mkdir(dir, dirname);
 }
 
-fs::vfs::dentry* fs::vfs_open(const char* path)
+fs::vfs::dentry* fs::vfs_open(
+    fs::vfs::dentry& root,
+    const char* pwd, const char* path)
 {
-    if (path[0] == '/' && path[1] == 0x00) {
-        return fs::fs_root;
-    }
+    fs::vfs::dentry* cur = nullptr;
+    if (!*path)
+        return nullptr;
 
-    auto* cur = fs::fs_root;
-    size_t n = 0;
-    switch (*(path++)) {
     // absolute path
-    case '/':
-        while (true) {
-            if (path[n] == 0x00) {
-                cur = cur->find(string(path, n));
-                return cur;
+    if (*path == '/' || !pwd)
+        cur = &root;
+    else
+        cur = vfs_open(root, nullptr, pwd);
+
+    size_t n = 0;
+    string curpath;
+    while (true) {
+        switch (path[n]) {
+        case '\0':
+            if (n != 0) {
+                curpath.clear();
+                curpath.append(path, n);
+                cur = cur->find(curpath);
             }
-            if (path[n] == '/') {
-                cur = cur->find(string(path, n));
-
-                if (!cur)
-                    return cur;
-
-                if (path[n + 1] == 0x00) {
-                    return cur;
-                } else {
-                    path += (n + 1);
-                    n = 0;
-                    continue;
-                }
+            return cur;
+
+        case '/':
+            if (n == 0) {
+                ++path;
+                continue;
             }
+
+            curpath.clear();
+            curpath.append(path, n);
+            cur = cur->find(curpath);
+
+            if (!cur)
+                return cur;
+
+            path += (n + 1);
+            n = 0;
+            break;
+
+        default:
             ++n;
+            break;
         }
-        break;
-    // empty string
-    case 0x00:
-        return nullptr;
-        break;
-    // relative path
-    default:
-        return nullptr;
-        break;
     }
-    return nullptr;
-}
-int fs::vfs_stat(const char* filename, stat* stat)
-{
-    auto ent = vfs_open(filename);
-    if (!ent)
-        return GB_FAILED;
-    return vfs_stat(ent, stat);
 }
+
 int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
 {
     return ent->ind->fs->inode_stat(ent, stat);
@@ -672,23 +703,14 @@ void init_vfs(void)
     vfs_mkdir(fs_root, "mnt");
     vfs_mkfile(fs_root, "init");
 
-    auto* init = vfs_open("/init");
+    auto* init = vfs_open(*fs_root, nullptr, "/init");
     assert(init);
     const char* str = "#/bin/sh\nexec /bin/sh\n";
     vfs_write(init->ind, str, 0, strlen(str));
 
-    auto* dev = vfs_open("/dev");
+    auto* dev = vfs_open(*fs_root, nullptr, "/dev");
     assert(dev);
     vfs_mknode(dev, "null", { .in { .major = 0, .minor = 0 } });
     vfs_mknode(dev, "console", { .in { .major = 1, .minor = 0 } });
     vfs_mknode(dev, "hda", { .in { .major = 2, .minor = 0 } });
-
-    stat _stat {};
-
-    vfs_stat("/init", &_stat);
-    vfs_stat("/", &_stat);
-    vfs_stat("/dev", &_stat);
-    vfs_stat("/dev/null", &_stat);
-    vfs_stat("/dev/console", &_stat);
-    vfs_stat("/dev/hda", &_stat);
 }

+ 3 - 2
src/types/elf.cpp

@@ -30,7 +30,7 @@ inline void _user_push(char** sp, const char* str)
 
 int types::elf::elf32_load(types::elf::elf32_load_data* d)
 {
-    auto* ent_exec = fs::vfs_open(d->exec);
+    auto* ent_exec = d->exec_dent;
     if (!ent_exec) {
         d->errcode = ENOENT;
         return GB_FAILED;
@@ -88,9 +88,10 @@ int types::elf::elf32_load(types::elf::elf32_load_data* d)
     // so we can't just simply return to it on error.
     current_process->mms.clear_user();
 
+    // TODO: remove this
     fs::inode* null_ind = nullptr;
     {
-        auto* dent = fs::vfs_open("/dev/null");
+        auto* dent = fs::vfs_open(*fs::fs_root, nullptr, "/dev/null");
         if (!dent)
             kill_current(-1);
         null_ind = dent->ind;

+ 4 - 2
user-space-program/lazybox.c

@@ -125,10 +125,12 @@ const char* find_file_name(const char* path)
 {
     const char* last = path + strlen(path);
     for (; last != path; --last) {
-        if (*last == '/')
+        if (*last == '/') {
+            ++last;
             break;
+        }
     }
-    return last + 1;
+    return last;
 }
 
 int parse_applet(const char* name)