diff --git a/Tests/LibC/CMakeLists.txt b/Tests/LibC/CMakeLists.txt index 25719f945b..18b60fb65a 100644 --- a/Tests/LibC/CMakeLists.txt +++ b/Tests/LibC/CMakeLists.txt @@ -5,6 +5,7 @@ set(TEST_SOURCES TestEnvironment.cpp TestIo.cpp TestLibCExec.cpp + TestLibCGrp.cpp TestLibCDirEnt.cpp TestLibCInodeWatcher.cpp TestLibCMkTemp.cpp diff --git a/Tests/LibC/TestLibCGrp.cpp b/Tests/LibC/TestLibCGrp.cpp new file mode 100644 index 0000000000..22fa7de269 --- /dev/null +++ b/Tests/LibC/TestLibCGrp.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-2023, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +void check_correctness(struct group* gr); + +void check_correctness(struct group* gr) +{ + EXPECT_NE(gr, nullptr); + EXPECT_EQ(gr->gr_gid, (gid_t)3); + EXPECT_EQ(gr->gr_name, "phys"sv); + EXPECT_EQ(gr->gr_passwd, "x"sv); + + HashTable members; + for (char** mem = gr->gr_mem; *mem; ++mem) { + members.set(ByteString(*mem)); + } + + EXPECT_EQ(true, members.contains("window"sv)); + EXPECT_EQ(true, members.contains("anon"sv)); +} + +TEST_CASE(getgrid_returns_correct_value) +{ + // From Base/etc/group + // phys:x:3:window,anon + struct group* gr = getgrgid(3); + + check_correctness(gr); + + gr = getgrgid(99999); + EXPECT_EQ(gr, nullptr); +} + +TEST_CASE(getgrid_r_uses_provided_buffer) +{ + // From Base/etc/group + // phys:x:3:window,anon + + struct group g; + struct group* res; + AK::Array buffer; + + setgrent(); + int rc = getgrgid_r(3, &g, buffer.data(), buffer.size(), &res); + endgrent(); + EXPECT_EQ(rc, 0); + check_correctness(&g); + EXPECT_EQ(res, &g); + + auto is_pointer_in_range = [&buffer](void* ptr) { + return (buffer.data() <= ptr) && (ptr < buffer.data() + buffer.size()); + }; + + EXPECT(is_pointer_in_range(g.gr_mem)); + EXPECT(is_pointer_in_range(g.gr_name)); + + char** mem = g.gr_mem; + for (; *mem; ++mem) { + EXPECT(is_pointer_in_range(mem)); + EXPECT(is_pointer_in_range(*mem)); + } + EXPECT(is_pointer_in_range(mem)); +} + +TEST_CASE(getgrname_r_uses_provided_buffer) +{ + // From Base/etc/group + // phys:x:3:window,anon + + struct group g; + struct group* res; + AK::Array buffer; + setgrent(); + int rc = getgrnam_r("phys", &g, buffer.data(), buffer.size(), &res); + endgrent(); + EXPECT_EQ(rc, 0); + check_correctness(&g); + EXPECT_EQ(res, &g); + + auto is_pointer_in_range = [&buffer](void* ptr) { + return (buffer.data() <= ptr) && (ptr < buffer.data() + buffer.size()); + }; + + EXPECT(is_pointer_in_range(g.gr_mem)); + EXPECT(is_pointer_in_range(g.gr_name)); + + char** mem = g.gr_mem; + for (; *mem; ++mem) { + EXPECT(is_pointer_in_range(mem)); + EXPECT(is_pointer_in_range(*mem)); + } + EXPECT(is_pointer_in_range(mem)); +} diff --git a/Userland/Libraries/LibC/grp.cpp b/Userland/Libraries/LibC/grp.cpp index 8157817d5e..6d9ef042ce 100644 --- a/Userland/Libraries/LibC/grp.cpp +++ b/Userland/Libraries/LibC/grp.cpp @@ -6,7 +6,9 @@ */ #include +#include #include +#include #include #include #include @@ -53,6 +55,16 @@ struct group* getgrgid(gid_t gid) return nullptr; } +int getgrgid_r(gid_t gid, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) +{ + while (0 == getgrent_r(group_buf, buffer, buffer_size, group_entry_ptr)) { + if (group_buf->gr_gid == gid) { + return 0; + } + } + return ENOENT; +} + struct group* getgrnam(char const* name) { setgrent(); @@ -63,6 +75,14 @@ struct group* getgrnam(char const* name) } return nullptr; } +int getgrnam_r(char const* name, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) +{ + while (0 == getgrent_r(group_buf, buffer, buffer_size, group_entry_ptr)) { + if (!strcmp(group_buf->gr_name, name)) + return 0; + } + return ENOENT; +} static bool parse_grpdb_entry(char* buffer, size_t buffer_size, struct group& group_entry) { diff --git a/Userland/Libraries/LibC/grp.h b/Userland/Libraries/LibC/grp.h index a3cbd61b59..f66ad2e2b5 100644 --- a/Userland/Libraries/LibC/grp.h +++ b/Userland/Libraries/LibC/grp.h @@ -25,7 +25,9 @@ int getgrent_r(struct group* group_buf, char* buffer, size_t buffer_size, struct void setgrent(void); void endgrent(void); struct group* getgrnam(char const* name); +int getgrnam_r(char const* name, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); struct group* getgrgid(gid_t); +int getgrgid_r(gid_t gid, struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); int putgrent(const struct group*, FILE*); int initgroups(char const* user, gid_t);