#include #include #include #include #include #include #include #include #include #include namespace os::FileSystem { bool exists(StringView path) { struct stat st; if (stat(path.chars(), &st) < 0) return false; return true; } bool is_directory(StringView path) { struct stat st; if (stat(path.chars(), &st) < 0) return false; return S_ISDIR(st.st_mode); } Result create_directory(StringView path, mode_t mode) { long rc = syscall(SYS_mkdir, path.chars(), mode); return Result::from_syscall(rc); } Result remove(StringView path) { return removeat(AT_FDCWD, path); } Result removeat(int dirfd, StringView path) { long rc = syscall(SYS_unlinkat, dirfd, path.chars(), 0); return Result::from_syscall(rc); } Result remove_tree(StringView path) { return remove_tree_at(AT_FDCWD, path); } Result remove_tree_at(int dirfd, StringView path) { auto rc = removeat(dirfd, path); if (!rc.has_error()) return {}; if (rc.error() != ENOTEMPTY) return rc.release_error(); DIR* dp = opendir(path.chars()); if (!dp) return err(errno); Vector entries; // FIXME: This is done because the kernel doesn't appreciate us deleting entries while iterating over // directories. This means that we have to iterate first, then delete. struct dirent* ent; while ((ent = readdir(dp))) { if ("."_sv == ent->d_name || ".."_sv == ent->d_name) continue; auto entry = TRY(String::from_cstring(ent->d_name)); TRY(entries.try_append(move(entry))); } for (const auto& entry : entries) { TRY(remove_tree_at(dp->_fd, entry.view())); } closedir(dp); return removeat(dirfd, path); } Result working_directory() { char* ptr = getcwd(NULL, 0); if (!ptr) return err(errno); return String { ptr }; } Result home_directory() { char* home = getenv("HOME"); if (home) return String::from_cstring(home); struct passwd* pw = getpwuid(getuid()); if (!pw) return err(ENOENT); return String::from_cstring(pw->pw_dir); } Result change_directory(StringView path) { long rc = syscall(SYS_chdir, path.chars()); return Result::from_syscall(rc); } }