libos: Introduce os::Directory

This commit is contained in:
apio 2023-04-28 21:15:41 +02:00
parent 8adfb6fdb9
commit 3e277b5d6f
Signed by: apio
GPG Key ID: B8A7D06E42258954
8 changed files with 149 additions and 28 deletions

View File

@ -1,10 +1,10 @@
#include <luna/PathParser.h> #include <luna/PathParser.h>
#include <luna/String.h> #include <luna/String.h>
#include <luna/Vector.h> #include <luna/Vector.h>
#include <os/Directory.h>
#include <os/File.h> #include <os/File.h>
#include <os/Process.h> #include <os/Process.h>
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
@ -237,24 +237,15 @@ static Result<void> load_service(StringView path)
static Result<void> load_services() static Result<void> load_services()
{ {
DIR* dp = opendir("/etc/init"); auto dir = TRY(os::Directory::open("/etc/init"));
if (!dp)
{
fprintf(g_init_log, "[init] cannot open service directory: %s\n", strerror(errno));
return {};
}
dirent* ent; String entry;
while ((ent = readdir(dp))) 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, entry.view()));
auto service_path = TRY(PathParser::join("/etc/init"_sv, ent->d_name));
TRY(load_service(service_path.view())); TRY(load_service(service_path.view()));
} }
closedir(dp);
return {}; return {};
} }

View File

@ -1,7 +1,6 @@
#include <os/ArgumentParser.h> #include <os/ArgumentParser.h>
#include <os/Directory.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -19,25 +18,22 @@ Result<int> luna_main(int argc, char** argv)
parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv); parser.add_switch_argument(show_almost_all, 'A', "almost-all"_sv, "list all files except '.' and '..'"_sv);
parser.parse(argc, argv); parser.parse(argc, argv);
DIR* dp = opendir(pathname.chars()); auto dir = TRY(os::Directory::open(pathname));
if (!dp)
{ auto filter = os::Directory::Filter::Hidden;
perror("opendir"); if (show_almost_all) filter = os::Directory::Filter::ParentAndBase;
return 1; else if (show_all)
} filter = os::Directory::Filter::None;
int first_ent = 1; int first_ent = 1;
do { do {
struct dirent* ent = readdir(dp); auto ent = TRY(dir->next(filter));
if (!ent) break; if (ent.is_empty()) break;
if (show_almost_all && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) continue; printf(first_ent ? "%s" : " %s", ent.chars());
if (!show_all && !show_almost_all && *ent->d_name == '.') continue;
printf(first_ent ? "%s" : " %s", ent->d_name);
first_ent = 0; first_ent = 0;
} while (1); } while (1);
putchar('\n'); putchar('\n');
closedir(dp);
return 0; return 0;
} }

View File

@ -1,3 +1,4 @@
#include "Log.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
@ -18,6 +19,8 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
if (basename.view() == ".") return err(EINVAL); 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)); auto inode = TRY(current->resolve_atfile(dirfd, dirname, false));
if (!VFS::can_write(inode, current->auth)) return err(EACCES); if (!VFS::can_write(inode, current->auth)) return err(EACCES);

View File

@ -22,8 +22,10 @@ extern "C"
#endif #endif
DIR* opendir(const char* path); DIR* opendir(const char* path);
DIR* fdopendir(int fd);
struct dirent* readdir(DIR* stream); struct dirent* readdir(DIR* stream);
int closedir(DIR* stream); int closedir(DIR* stream);
int dirfd(DIR* stream);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -25,6 +25,16 @@ extern "C"
return dp; 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) struct dirent* readdir(DIR* stream)
{ {
// FIXME: Very hackish, right now luna_dirent and dirent have the same layout. // FIXME: Very hackish, right now luna_dirent and dirent have the same layout.
@ -51,4 +61,9 @@ extern "C"
return 0; return 0;
} }
int dirfd(DIR* stream)
{
return stream->_fd;
}
} }

View File

@ -8,6 +8,7 @@ set(SOURCES
src/File.cpp src/File.cpp
src/FileSystem.cpp src/FileSystem.cpp
src/Process.cpp src/Process.cpp
src/Directory.cpp
src/Main.cpp src/Main.cpp
) )

View File

@ -0,0 +1,36 @@
#pragma once
#include <dirent.h>
#include <luna/SharedPtr.h>
#include <luna/String.h>
namespace os
{
class Directory
{
public:
static Result<SharedPtr<Directory>> open(StringView path);
static Result<SharedPtr<Directory>> openat(int dirfd, StringView path);
enum class Filter
{
None,
Hidden,
ParentAndBase
};
Result<String> next(Filter filter);
int fd()
{
return dirfd(m_dirp);
}
~Directory();
private:
Directory(Badge<Directory>);
DIR* m_dirp { nullptr };
};
}

77
libos/src/Directory.cpp Normal file
View File

@ -0,0 +1,77 @@
#include <luna/CString.h>
#include <os/Directory.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
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<SharedPtr<Directory>> 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<SharedPtr<Directory>> 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<int>::from_syscall(rc));
DIR* dp = fdopendir(fd);
if (!dp)
{
close(fd);
return err(errno);
}
dir->m_dirp = dp;
return dir;
}
Result<String> 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<Directory>)
{
}
}