diff --git a/apps/init.cpp b/apps/init.cpp index f8440f95..76634064 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -1,10 +1,10 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -237,24 +237,15 @@ static Result load_service(StringView path) static Result load_services() { - DIR* dp = opendir("/etc/init"); - if (!dp) - { - fprintf(g_init_log, "[init] cannot open service directory: %s\n", strerror(errno)); - return {}; - } + auto dir = TRY(os::Directory::open("/etc/init")); - dirent* ent; - while ((ent = readdir(dp))) + String entry; + while ((entry = TRY(dir->next(os::Directory::Filter::ParentAndBase))), !entry.is_empty()) { - if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue; - - auto service_path = TRY(PathParser::join("/etc/init"_sv, ent->d_name)); + auto service_path = TRY(PathParser::join("/etc/init"_sv, entry.view())); TRY(load_service(service_path.view())); } - closedir(dp); - return {}; } diff --git a/apps/ls.cpp b/apps/ls.cpp index e2f412ed..72820193 100644 --- a/apps/ls.cpp +++ b/apps/ls.cpp @@ -1,7 +1,6 @@ #include +#include -#include -#include #include #include @@ -19,25 +18,22 @@ Result luna_main(int argc, char** argv) parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv); parser.parse(argc, argv); - DIR* dp = opendir(pathname.chars()); - if (!dp) - { - perror("opendir"); - return 1; - } + auto dir = TRY(os::Directory::open(pathname)); + + auto filter = os::Directory::Filter::Hidden; + if (show_almost_all) filter = os::Directory::Filter::ParentAndBase; + else if (show_all) + filter = os::Directory::Filter::None; int first_ent = 1; do { - struct dirent* ent = readdir(dp); - if (!ent) break; - if (show_almost_all && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) continue; - if (!show_all && !show_almost_all && *ent->d_name == '.') continue; - printf(first_ent ? "%s" : " %s", ent->d_name); + auto ent = TRY(dir->next(filter)); + if (ent.is_empty()) break; + printf(first_ent ? "%s" : " %s", ent.chars()); first_ent = 0; } while (1); putchar('\n'); - closedir(dp); return 0; } diff --git a/kernel/src/sys/link.cpp b/kernel/src/sys/link.cpp index 0a5d6768..7dc3fc78 100644 --- a/kernel/src/sys/link.cpp +++ b/kernel/src/sys/link.cpp @@ -1,3 +1,4 @@ +#include "Log.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -18,6 +19,8 @@ Result sys_unlinkat(Registers*, SyscallArgs args) if (basename.view() == ".") return err(EINVAL); + kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd); + auto inode = TRY(current->resolve_atfile(dirfd, dirname, false)); if (!VFS::can_write(inode, current->auth)) return err(EACCES); diff --git a/libc/include/dirent.h b/libc/include/dirent.h index f2927430..07c8566d 100644 --- a/libc/include/dirent.h +++ b/libc/include/dirent.h @@ -22,8 +22,10 @@ extern "C" #endif DIR* opendir(const char* path); + DIR* fdopendir(int fd); struct dirent* readdir(DIR* stream); int closedir(DIR* stream); + int dirfd(DIR* stream); #ifdef __cplusplus } diff --git a/libc/src/dirent.cpp b/libc/src/dirent.cpp index b0c98e8f..cf81dceb 100644 --- a/libc/src/dirent.cpp +++ b/libc/src/dirent.cpp @@ -25,6 +25,16 @@ extern "C" return dp; } + DIR* fdopendir(int fd) + { + DIR* dp = (DIR*)malloc(sizeof(DIR)); + if (!dp) { return nullptr; } + + dp->_fd = fd; + + return dp; + } + struct dirent* readdir(DIR* stream) { // FIXME: Very hackish, right now luna_dirent and dirent have the same layout. @@ -51,4 +61,9 @@ extern "C" return 0; } + + int dirfd(DIR* stream) + { + return stream->_fd; + } } diff --git a/libos/CMakeLists.txt b/libos/CMakeLists.txt index 91127a5e..0e9b441d 100644 --- a/libos/CMakeLists.txt +++ b/libos/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES src/File.cpp src/FileSystem.cpp src/Process.cpp + src/Directory.cpp src/Main.cpp ) diff --git a/libos/include/os/Directory.h b/libos/include/os/Directory.h new file mode 100644 index 00000000..eaa68d9c --- /dev/null +++ b/libos/include/os/Directory.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + +namespace os +{ + class Directory + { + public: + static Result> open(StringView path); + + static Result> openat(int dirfd, StringView path); + + enum class Filter + { + None, + Hidden, + ParentAndBase + }; + + Result next(Filter filter); + + int fd() + { + return dirfd(m_dirp); + } + + ~Directory(); + + private: + Directory(Badge); + + DIR* m_dirp { nullptr }; + }; +} diff --git a/libos/src/Directory.cpp b/libos/src/Directory.cpp new file mode 100644 index 00000000..5003ae25 --- /dev/null +++ b/libos/src/Directory.cpp @@ -0,0 +1,77 @@ +#include +#include + +#include +#include +#include +#include + +static bool should_skip_entry(const char* name, os::Directory::Filter filter) +{ + if (filter == os::Directory::Filter::None) return false; + + if (filter == os::Directory::Filter::Hidden) return *name == '.'; + + if (filter == os::Directory::Filter::ParentAndBase) return !strcmp(name, ".") || !strcmp(name, ".."); + + unreachable(); +} + +namespace os +{ + Result> Directory::open(StringView path) + { + auto dir = TRY(adopt_shared_if_nonnull(new (std::nothrow) Directory({}))); + + DIR* dp = opendir(path.chars()); + if (!dp) return err(errno); + + dir->m_dirp = dp; + + return dir; + } + + Result> Directory::openat(int dirfd, StringView path) + { + auto dir = TRY(adopt_shared_if_nonnull(new (std::nothrow) Directory({}))); + + long rc = syscall(SYS_openat, dirfd, path.chars(), O_RDONLY | O_DIRECTORY, 0); + int fd = TRY(Result::from_syscall(rc)); + + DIR* dp = fdopendir(fd); + if (!dp) + { + close(fd); + return err(errno); + } + + dir->m_dirp = dp; + + return dir; + } + + Result Directory::next(Filter filter) + { + errno = 0; + + struct dirent* ent; + do { + ent = readdir(m_dirp); + } while (ent && should_skip_entry(ent->d_name, filter)); + + if (!ent && errno) return err(errno); + + if (!ent) return String {}; + + return String::from_cstring(ent->d_name); + } + + Directory::~Directory() + { + if (m_dirp) closedir(m_dirp); + } + + Directory::Directory(Badge) + { + } +}