Kernel/LibC: Implement {get,set,init}groups

This allows dropping /etc/group parsing from the kernel :D
This commit is contained in:
2025-08-10 18:19:31 +03:00
parent f41e254e35
commit af0bca74e4
9 changed files with 150 additions and 161 deletions

View File

@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
static FILE* s_grent_fp = nullptr;
static group s_grent_struct;
@@ -211,3 +212,106 @@ int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize
fclose(fp);
return ret;
}
static gid_t* get_user_groups(const char* user)
{
FILE* fp = fopen("/etc/group", "r");
if (fp == nullptr)
return nullptr;
const long initial_len = sysconf(_SC_GETGR_R_SIZE_MAX);
size_t buffer_len = (initial_len == -1) ? 512 : initial_len;
char* buffer = static_cast<char*>(malloc(buffer_len));
if (buffer == nullptr)
{
fclose(fp);
return nullptr;
}
size_t group_count = 0;
gid_t* groups = static_cast<gid_t*>(malloc(sizeof(gid_t)));
if (groups == nullptr)
{
free(buffer);
fclose(fp);
return nullptr;
}
for (;;)
{
struct group result;
struct group* resultp;
int error = getgrent_impl(fp, &result, buffer, buffer_len, &resultp);
if (error == ERANGE)
{
const size_t new_buffer_len = buffer_len * 2;
char* new_buffer = static_cast<char*>(realloc(buffer, new_buffer_len));
if (new_buffer == nullptr)
{
error = ENOMEM;
break;
}
buffer = new_buffer;
buffer_len = new_buffer_len;
continue;
}
if (error != 0)
{
free(buffer);
free(groups);
fclose(fp);
return nullptr;
}
if (resultp == nullptr)
break;
bool contains = false;
for (size_t i = 0; result.gr_mem[i] && !contains; i++)
contains = (strcmp(result.gr_mem[i], user) == 0);
if (!contains)
continue;
gid_t* new_groups = static_cast<gid_t*>(realloc(groups, group_count * sizeof(gid_t)));
if (new_groups == nullptr)
{
free(buffer);
free(groups);
fclose(fp);
return nullptr;
}
groups = new_groups;
groups[group_count++] = result.gr_gid;
}
groups[group_count] = -1;
free(buffer);
fclose(fp);
return groups;
}
int initgroups(const char* user, gid_t group)
{
gid_t* groups = get_user_groups(user);
if (groups == nullptr)
return -1;
size_t group_count = 0;
while (groups[group_count] != -1)
group_count++;
groups[group_count] = group;
int result = setgroups(group_count + 1, groups);
free(groups);
return result;
}
int setgroups(size_t size, const gid_t list[])
{
return syscall(SYS_SETGROUPS, list, size);
}

View File

@@ -14,7 +14,7 @@ __BEGIN_DECLS
struct group
{
char* gr_name; /* The name of the group. */
char* gr_passwd;/* The password 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. */
};
@@ -27,6 +27,9 @@ 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);
int initgroups(const char* user, gid_t group);
int setgroups(size_t size, const gid_t list[]);
__END_DECLS
#endif

View File

@@ -111,6 +111,8 @@ __BEGIN_DECLS
O(SYS_FLOCK, flock) \
O(SYS_GET_NPROCESSOR, get_nprocessor) \
O(SYS_FUTEX, futex) \
O(SYS_GETGROUPS, getgroups) \
O(SYS_SETGROUPS, setgroups) \
enum Syscall
{

View File

@@ -673,8 +673,7 @@ error:
int getgroups(int gidsetsize, gid_t grouplist[])
{
dwarnln("FIXME: getgroups({}, {})", gidsetsize, grouplist);
return 0;
return syscall(SYS_GETGROUPS, grouplist, gidsetsize);
}
pid_t getppid(void)