From 668c4c897671ed9d8a2836829c0e2744289d1a38 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 3 Jan 2024 00:14:49 +0200 Subject: [PATCH] LibC: Implement getgrnam and getgrgid --- libc/CMakeLists.txt | 1 + libc/grp.cpp | 162 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 libc/grp.cpp diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index c7e3a21d..3b2d634d 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -7,6 +7,7 @@ set(LIBC_SOURCES ctype.cpp dirent.cpp fcntl.cpp + grp.cpp malloc.cpp printf_impl.cpp pwd.cpp diff --git a/libc/grp.cpp b/libc/grp.cpp new file mode 100644 index 00000000..b7547ee0 --- /dev/null +++ b/libc/grp.cpp @@ -0,0 +1,162 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static struct stat s_group_st; +static char* s_group_mmap = nullptr; +static int s_group_fd = -1; + +static struct group s_group; + +id_t parse_id(BAN::StringView string) +{ + id_t id = 0; + for (char c : string) + { + if (!isdigit(c)) + return -1; + id = (id * 10) + (c - '0'); + } + return id; +} + +static bool open_group_file() +{ + if (s_group_fd == -1) + s_group_fd = open("/etc/group", O_RDONLY); + if (s_group_fd == -1) + return false; + + if (fstat(s_group_fd, &s_group_st) == -1) + return false; + + if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED) + s_group_mmap = (char*)mmap(nullptr, s_group_st.st_size, PROT_READ, MAP_PRIVATE, s_group_fd, 0); + if (s_group_mmap == MAP_FAILED) + return false; + + s_group.gr_name = nullptr; + s_group.gr_mem = nullptr; + + return true; +} + +struct group* fill_group(const BAN::Vector& parts) +{ + if (parts.size() != 4) + return nullptr; + + if (s_group.gr_name) + { + free(s_group.gr_name); + s_group.gr_name = nullptr; + } + + if (s_group.gr_mem) + { + for (size_t i = 0; s_group.gr_mem && s_group.gr_mem[i]; i++) + free(s_group.gr_mem[i]); + free(s_group.gr_mem); + s_group.gr_mem = nullptr; + } + + auto groups_or_error = parts[3].split(','); + if (groups_or_error.is_error()) + return nullptr; + auto groups = groups_or_error.release_value(); + + s_group.gr_gid = parse_id(parts[2]); + if (s_group.gr_gid == -1) + return nullptr; + + s_group.gr_name = (char*)malloc(parts[0].size() + 1); + if (s_group.gr_name == nullptr) + return nullptr; + memcpy(s_group.gr_name, parts[0].data(), parts[0].size()); + s_group.gr_name[parts[0].size()] = '\0'; + + s_group.gr_mem = (char**)malloc((groups.size() + 1) * sizeof(char*)); + if (s_group.gr_mem == nullptr) + return nullptr; + + for (size_t i = 0; i < groups.size(); i++) + { + s_group.gr_mem[i] = (char*)malloc(groups[i].size() + 1); + if (s_group.gr_mem[i] == nullptr) + { + for (size_t j = 0; j < i; j++) + free(s_group.gr_mem[j]); + free(s_group.gr_mem); + s_group.gr_mem = nullptr; + return nullptr; + } + memcpy(s_group.gr_mem[i], groups[i].data(), groups[i].size()); + s_group.gr_mem[i][groups[i].size()] = '\0'; + } + s_group.gr_mem[groups.size()] = nullptr; + + return &s_group; +} + +struct group* getgrnam(const char* name) +{ + if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED) + if (!open_group_file()) + return nullptr; + + off_t start = 0; + off_t end = 0; + while (start < s_group_st.st_size) + { + while (end < s_group_st.st_size && s_group_mmap[end] != '\n') + end++; + + BAN::StringView line(s_group_mmap + start, end - start); + start = ++end; + + auto parts_or_error = line.split(':', true); + if (parts_or_error.is_error()) + return nullptr; + + auto parts = parts_or_error.release_value(); + if (parts.size() == 4 && parts[0] == name) + return fill_group(parts); + } + + return nullptr; +} + +struct group* getgrgid(gid_t gid) +{ + if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED) + if (!open_group_file()) + return nullptr; + + off_t start = 0; + off_t end = 0; + while (start < s_group_st.st_size) + { + while (end < s_group_st.st_size && s_group_mmap[end] != '\n') + end++; + + BAN::StringView line(s_group_mmap + start, end - start); + start = ++end; + + auto parts_or_error = line.split(':', true); + if (parts_or_error.is_error()) + return nullptr; + + auto parts = parts_or_error.release_value(); + if (parts.size() == 4 && parse_id(parts[2]) == gid) + return fill_group(parts); + } + + return nullptr; +}