diff --git a/userspace/libraries/LibC/grp.cpp b/userspace/libraries/LibC/grp.cpp index 4e19220f..b31b633e 100644 --- a/userspace/libraries/LibC/grp.cpp +++ b/userspace/libraries/LibC/grp.cpp @@ -1,162 +1,213 @@ -#include -#include - #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 FILE* s_grent_fp = nullptr; +static group s_grent_struct; -static struct group s_group; +static char* s_grent_buffer = nullptr; +static size_t s_grent_buffer_size = 0; -id_t parse_id(BAN::StringView string) +void endgrent(void) { - id_t id = 0; - for (char c : string) - { - if (!isdigit(c)) - return -1; - id = (id * 10) + (c - '0'); - } - return id; + if (s_grent_fp) + fclose(s_grent_fp); + s_grent_fp = nullptr; + + if (s_grent_buffer) + free(s_grent_buffer); + s_grent_buffer = nullptr; } -static bool open_group_file() +void setgrent(void) { - 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; + if (!s_grent_fp) + return; + fseek(s_grent_fp, 0, SEEK_SET); } -struct group* fill_group(const BAN::Vector& parts) +static int getgrent_impl(FILE* fp, struct group* grp, char* buffer, size_t bufsize, struct group** result) { - if (parts.size() != 4) - return nullptr; - - if (s_group.gr_name) + for (;;) { - 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) + if (fgets(buffer, bufsize, fp) == 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; + if (ferror(fp)) + return errno; + *result = nullptr; + return 0; } - 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; + 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; + + grp->gr_name = ptr; + GET_STRING(); + + grp->gr_passwd = ptr; + GET_STRING(); + + grp->gr_gid = GET_INT(); + + size_t offset = line_len + 1; + if (auto rem = offset % alignof(char*)) + offset += alignof(char*) - rem; + grp->gr_mem = reinterpret_cast(buffer + offset); + + const size_t mem_max = (bufsize - offset) / sizeof(char*); + size_t mem_idx = 0; + + while (*ptr && mem_idx + 1 <= mem_max) + { + grp->gr_mem[mem_idx++] = ptr; + + ptr = strchrnul(ptr, ','); + if (*ptr == ',') + *ptr++ = '\0'; + } + + if (mem_idx + 1 > mem_max) + return (errno = ERANGE); + + grp->gr_mem[mem_idx] = nullptr; + *result = grp; + return 0; + } } -struct group* getgrnam(const char* name) +struct group* getgrent(void) { - 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) + if (s_grent_fp == nullptr) { - 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()) + s_grent_fp = fopen("/etc/group", "r"); + if (s_grent_fp == nullptr) return nullptr; - - auto parts = parts_or_error.release_value(); - if (parts.size() == 4 && parts[0] == name) - return fill_group(parts); } - return nullptr; + if (s_grent_buffer == nullptr) + { + long size = sysconf(_SC_GETGR_R_SIZE_MAX); + if (size == -1) + size = 512; + + s_grent_buffer = static_cast(malloc(size)); + if (s_grent_buffer == nullptr) + return nullptr; + s_grent_buffer_size = size; + } + + const off_t old_offset = ftello(s_grent_fp); + + group* result; + for (;;) + { + const int error = getgrent_impl(s_grent_fp, &s_grent_struct, s_grent_buffer, s_grent_buffer_size, &result); + if (error == 0) + break; + fseeko(s_grent_fp, old_offset, SEEK_SET); + if (error != ERANGE) + return nullptr; + + const size_t new_size = s_grent_buffer_size * 2; + char* new_buffer = static_cast(realloc(s_grent_buffer, new_size)); + if (new_buffer == nullptr) + return nullptr; + + s_grent_buffer = new_buffer; + s_grent_buffer_size = new_size; + } + + return result; } 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); - } - + group* grp; + setgrent(); + while ((grp = getgrent())) + if (grp->gr_gid == gid) + return grp; return nullptr; } + +struct group* getgrnam(const char* name) +{ + group* grp; + setgrent(); + while ((grp = getgrent())) + if (strcmp(grp->gr_name, name) == 0) + return grp; + return nullptr; +} + +int getgrgid_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result) +{ + FILE* fp = fopen("/etc/group", "r"); + if (fp == nullptr) + return errno; + + int ret = 0; + for (;;) + { + if ((ret = getgrent_impl(fp, grp, buffer, bufsize, result))) + break; + if (*result == nullptr) + break; + if (grp->gr_gid == gid) + break; + } + + fclose(fp); + return ret; +} + +int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result) +{ + FILE* fp = fopen("/etc/group", "r"); + if (fp == nullptr) + return errno; + + int ret = 0; + for (;;) + { + if ((ret = getgrent_impl(fp, grp, buffer, bufsize, result))) + break; + if (*result == nullptr) + break; + if (strcmp(grp->gr_name, name) == 0) + break; + } + + fclose(fp); + return ret; +} diff --git a/userspace/libraries/LibC/include/grp.h b/userspace/libraries/LibC/include/grp.h index 9d04e2ed..7d8448fb 100644 --- a/userspace/libraries/LibC/include/grp.h +++ b/userspace/libraries/LibC/include/grp.h @@ -13,15 +13,16 @@ __BEGIN_DECLS struct group { - char* gr_name; /* The name of the group. */ - gid_t gr_gid; /* Numerical group ID. */ - char** gr_mem; /* Pointer to a null-terminated array of character pointers to member names. */ + char* gr_name; /* The name of the group. */ + char* gr_passwd;/* The password of the group */ + gid_t gr_gid; /* Numerical group ID. */ + char** gr_mem; /* Pointer to a null-terminated array of character pointers to member names. */ }; void endgrent(void); struct group* getgrent(void); struct group* getgrgid(gid_t gid); -int getgrgit_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result); +int getgrgid_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result); struct group* getgrnam(const char* name); int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result); void setgrent(void);