From fda0ced72e43dd0b026ba9e9203ac90c104814b0 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 29 Jul 2025 14:49:08 +0300 Subject: [PATCH] LibC: Implement getpw{nam,uid}_r --- userspace/libraries/LibC/pwd.cpp | 255 +++++++++++++++++++------------ 1 file changed, 157 insertions(+), 98 deletions(-) diff --git a/userspace/libraries/LibC/pwd.cpp b/userspace/libraries/LibC/pwd.cpp index 3e363b93..9d9ae161 100644 --- a/userspace/libraries/LibC/pwd.cpp +++ b/userspace/libraries/LibC/pwd.cpp @@ -1,118 +1,142 @@ -#include - #include #include #include -#include #include #include +#include static FILE* s_pwent_fp = nullptr; static passwd s_pwent_struct; -static bool open_pwent() -{ - if (s_pwent_fp) - return true; - s_pwent_fp = fopen("/etc/passwd", "r"); - return s_pwent_fp; -} - -static void clear_pwent(passwd& passwd) -{ - if (passwd.pw_name) - free(passwd.pw_name); - passwd.pw_name = nullptr; - if (passwd.pw_dir) - free(passwd.pw_dir); - passwd.pw_dir = nullptr; - if (passwd.pw_shell) - free(passwd.pw_shell); - passwd.pw_shell = nullptr; -} +static char* s_pwent_buffer = nullptr; +static size_t s_pwent_buffer_size = 0; void endpwent(void) +{ + if (s_pwent_fp) + fclose(s_pwent_fp); + s_pwent_fp = nullptr; + + if (s_pwent_buffer) + free(s_pwent_buffer); + s_pwent_buffer = nullptr; +} + +void setpwent(void) { if (!s_pwent_fp) return; - fclose(s_pwent_fp); - s_pwent_fp = nullptr; - clear_pwent(s_pwent_struct); + fseek(s_pwent_fp, 0, SEEK_SET); +} + +static int getpwent_impl(FILE* fp, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) +{ + for (;;) + { + if (fgets(buffer, bufsize, fp) == nullptr) + { + if (ferror(fp)) + return errno; + *result = nullptr; + return 0; + } + + const size_t line_len = strlen(buffer); + if (line_len == 0) + continue; + + if (buffer[line_len - 1] == '\n') + buffer[line_len - 1] = '\0'; + else if (!feof(fp)) + return (errno = ERANGE); + +#define GET_STRING() ({ \ + ptr = strchr(ptr, ':'); \ + if (ptr == nullptr) \ + continue; \ + *ptr++ = '\0'; \ + }) + +#define GET_INT() ({ \ + if (!isdigit(*ptr)) \ + continue; \ + long val = 0; \ + while (isdigit(*ptr)) \ + val = (val * 10) + (*ptr++ - '0'); \ + if (*ptr != ':') \ + continue; \ + *ptr++ = '\0'; \ + val; \ + }) + + char* ptr = buffer; + + pwd->pw_name = ptr; + GET_STRING(); + + pwd->pw_passwd = ptr; + GET_STRING(); + + pwd->pw_uid = GET_INT(); + + pwd->pw_gid = GET_INT(); + + pwd->pw_gecos = ptr; + GET_STRING(); + + pwd->pw_dir = ptr; + GET_STRING(); + + pwd->pw_shell = ptr; + + *result = pwd; + return 0; + } } struct passwd* getpwent(void) { - if (!s_pwent_fp) - if (!open_pwent()) - return nullptr; - clear_pwent(s_pwent_struct); - - static char buffer[4096]; - if (!fgets(buffer, sizeof(buffer), s_pwent_fp)) - return nullptr; - - size_t buffer_len = strlen(buffer); - - ASSERT(buffer[buffer_len - 1] == '\n'); - buffer[buffer_len - 1] = '\0'; - buffer_len--; - - const char* ptr = buffer; - for (int i = 0; i < 7; i++) + if (s_pwent_fp == nullptr) { - char* end = strchr(ptr, ':'); - ASSERT((i < 6) ? end != nullptr : end == nullptr); - if (!end) - end = buffer + buffer_len; - *end = '\0'; - - const size_t field_len = end - ptr; - - switch (i) - { - case 0: - s_pwent_struct.pw_name = strndup(ptr, field_len + 1); - if (!s_pwent_struct.pw_name) - return nullptr; - break; - case 1: - s_pwent_struct.pw_passwd = strndup(ptr, field_len + 1); - if (!s_pwent_struct.pw_passwd) - return nullptr; - break; - case 2: - ASSERT(1 <= field_len && field_len <= 9); - for (size_t j = 0; j < field_len; j++) - ASSERT(isdigit(ptr[j])); - s_pwent_struct.pw_uid = atoi(ptr); - break; - case 3: - ASSERT(1 <= field_len && field_len <= 9); - for (size_t j = 0; j < field_len; j++) - ASSERT(isdigit(ptr[j])); - s_pwent_struct.pw_gid = atoi(ptr); - break; - case 4: - s_pwent_struct.pw_gecos = strndup(ptr, field_len + 1); - if (!s_pwent_struct.pw_gecos) - return nullptr; - break; - case 5: - s_pwent_struct.pw_dir = strndup(ptr, field_len + 1); - if (!s_pwent_struct.pw_dir) - return nullptr; - break; - case 6: - s_pwent_struct.pw_shell = strndup(ptr, field_len + 1); - if (!s_pwent_struct.pw_shell) - return nullptr; - break; - } - - ptr = end + 1; + s_pwent_fp = fopen("/etc/passwd", "r"); + if (s_pwent_fp == nullptr) + return nullptr; } - return &s_pwent_struct; + if (s_pwent_buffer == nullptr) + { + long size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size == -1) + size = 512; + + s_pwent_buffer = static_cast(malloc(size)); + if (s_pwent_buffer == nullptr) + return nullptr; + s_pwent_buffer_size = size; + } + + const off_t old_offset = ftello(s_pwent_fp); + + passwd* result; + for (;;) + { + const int error = getpwent_impl(s_pwent_fp, &s_pwent_struct, s_pwent_buffer, s_pwent_buffer_size, &result); + if (error == 0) + break; + fseeko(s_pwent_fp, old_offset, SEEK_SET); + if (error != ERANGE) + return nullptr; + + const size_t new_size = s_pwent_buffer_size * 2; + char* new_buffer = static_cast(realloc(s_pwent_buffer, new_size)); + if (new_buffer == nullptr) + return nullptr; + + s_pwent_buffer = new_buffer; + s_pwent_buffer_size = new_size; + } + + return result; } struct passwd* getpwnam(const char* name) @@ -135,9 +159,44 @@ struct passwd* getpwuid(uid_t uid) return nullptr; } -void setpwent(void) +int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) { - if (!s_pwent_fp) - return; - fseek(s_pwent_fp, 0, SEEK_SET); + FILE* fp = fopen("/etc/passwd", "r"); + if (fp == nullptr) + return errno; + + int ret = 0; + for (;;) + { + if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result))) + break; + if (*result == nullptr) + break; + if (pwd->pw_uid == uid) + break; + } + + fclose(fp); + return ret; +} + +int getpwnam_r(const char* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result) +{ + FILE* fp = fopen("/etc/passwd", "r"); + if (fp == nullptr) + return errno; + + int ret = 0; + for (;;) + { + if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result))) + break; + if (*result == nullptr) + break; + if (strcmp(pwd->pw_name, name) == 0) + break; + } + + fclose(fp); + return ret; }