diff --git a/Userland/Libraries/LibC/grp.cpp b/Userland/Libraries/LibC/grp.cpp index 7d90833e9d..f3a5501b31 100644 --- a/Userland/Libraries/LibC/grp.cpp +++ b/Userland/Libraries/LibC/grp.cpp @@ -20,12 +20,6 @@ extern "C" { static FILE* s_stream = nullptr; static unsigned s_line_number = 0; -static struct group s_group; - -static String s_name; -static String s_passwd; -static Vector s_members; -static Vector s_members_ptrs; void setgrent() { @@ -71,67 +65,120 @@ struct group* getgrnam(char const* name) return nullptr; } -static bool parse_grpdb_entry(String const& line) +static bool parse_grpdb_entry(char* buffer, size_t buffer_size, struct group& group_entry) { - auto parts = line.split_view(':', SplitBehavior::KeepEmpty); + size_t line_length = strlen(buffer); + + for (size_t i = 0; i < line_length; ++i) { + auto& ch = buffer[i]; + if (ch == '\r' || ch == '\n') + line_length = i; + if (ch == ':' || ch == '\r' || ch == '\n') + ch = '\0'; + } + + auto line = StringView { buffer, line_length }; + auto parts = line.split_view('\0', SplitBehavior::KeepEmpty); if (parts.size() != 4) { - warnln("getgrent(): Malformed entry on line {}: '{}' has {} parts", s_line_number, line, parts.size()); + warnln("parse_grpdb_entry(): Malformed entry on line {}: '{}' has {} parts", s_line_number, line, parts.size()); return false; } - s_name = parts[0]; - s_passwd = parts[1]; - + auto name = parts[0]; + auto passwd = parts[1]; auto& gid_string = parts[2]; - String members_string = parts[3]; + StringView members_string = parts[3]; auto gid = gid_string.to_uint(); if (!gid.has_value()) { - warnln("getgrent(): Malformed GID on line {}", s_line_number); + warnln("parse_grpdb_entry(): Malformed GID on line {}", s_line_number); return false; } - s_members = members_string.split(','); - s_members_ptrs.clear_with_capacity(); - s_members_ptrs.ensure_capacity(s_members.size() + 1); - for (auto& member : s_members) { - s_members_ptrs.append(member.characters()); + // Generate table of members pointers. + Vector members_ptrs; + auto members = members_string.split_view(','); + members_ptrs.clear_with_capacity(); + members_ptrs.ensure_capacity(members.size() + 1); + for (auto& member : members) { + members_ptrs.append(member.characters_without_null_termination()); } - s_members_ptrs.append(nullptr); + members_ptrs.append(nullptr); - s_group.gr_gid = gid.value(); - s_group.gr_name = const_cast(s_name.characters()); - s_group.gr_passwd = const_cast(s_passwd.characters()); - s_group.gr_mem = const_cast(s_members_ptrs.data()); + // Convert remaining commas to null terminators. Last gr_mem entry uses the whole line's null terminator. + // 3 for 3 null terminators. + size_t members_position = name.length() + passwd.length() + gid_string.length() + 3; + for (size_t i = members_position; i < line_length; i++) + if (buffer[i] == ',') + buffer[i] = '\0'; - return true; + // Must have room at the end of the buffer for the new table. + // Remaining space is one byte past null terminator generated by original line. + size_t bytes_used = line_length + 1; + size_t ptrs_size = sizeof(char const*) * members_ptrs.size(); + + if (bytes_used + ptrs_size < buffer_size) { + char* buffer_remaining = buffer + bytes_used; + memcpy(buffer_remaining, members_ptrs.data(), ptrs_size); + + group_entry.gr_gid = gid.value(); + group_entry.gr_name = const_cast(name.characters_without_null_termination()); + group_entry.gr_passwd = const_cast(passwd.characters_without_null_termination()); + group_entry.gr_mem = reinterpret_cast(buffer_remaining); + + return true; + } else { + warnln("parse_grpdb_entry(): Provided buffer too small to fit table for gr_mem"); + errno = ERANGE; + return false; + } } struct group* getgrent() +{ + static struct group group_entry; + static char buffer[1024]; + struct group* result; + if (getgrent_r(&group_entry, buffer, sizeof(buffer), &result) < 0) + return nullptr; + return result; +} + +int getgrent_r(struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr) { if (!s_stream) setgrent(); while (true) { - if (!s_stream || feof(s_stream)) - return nullptr; - - if (ferror(s_stream)) { - warnln("getgrent(): Read error: {}", strerror(ferror(s_stream))); - return nullptr; + if (!s_stream || feof(s_stream)) { + errno = EIO; + return -1; + } + + if (ferror(s_stream)) { + warnln("getgrent_r(): Read error: {}", strerror(ferror(s_stream))); + errno = EIO; + return -1; } - char buffer[1024]; ++s_line_number; - char* s = fgets(buffer, sizeof(buffer), s_stream); + char* s = fgets(buffer, buffer_size, s_stream); // Silently tolerate an empty line at the end. - if ((!s || !s[0]) && feof(s_stream)) - return nullptr; + if ((!s || !s[0]) && feof(s_stream)) { + *group_entry_ptr = nullptr; + return 0; + } - String line(s, Chomp); - if (parse_grpdb_entry(line)) - return &s_group; + if (strlen(s) == buffer_size - 1) { + errno = ERANGE; + return -1; + } + + if (parse_grpdb_entry(buffer, buffer_size, *group_buf)) { + *group_entry_ptr = group_buf; + return 0; + } // Otherwise, proceed to the next line. } } diff --git a/Userland/Libraries/LibC/grp.h b/Userland/Libraries/LibC/grp.h index 8fefd99811..a3cbd61b59 100644 --- a/Userland/Libraries/LibC/grp.h +++ b/Userland/Libraries/LibC/grp.h @@ -21,6 +21,7 @@ struct group { }; struct group* getgrent(void); +int getgrent_r(struct group* group_buf, char* buffer, size_t buffer_size, struct group** group_entry_ptr); void setgrent(void); void endgrent(void); struct group* getgrnam(char const* name);