user: implement mlibc as the libc, finally.

It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-02 03:31:49 -04:00
parent 2fa39ad85a
commit 9a9b91c940
2387 changed files with 152741 additions and 315 deletions
@@ -0,0 +1,309 @@
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <bits/ensure.h>
#include <mlibc/debug.hpp>
namespace {
FILE *global_file; // Used by setpwent/getpwent/endpwent.
bool open_global_file() {
if(!global_file) {
global_file = fopen("/etc/passwd", "r");
if(!global_file) {
errno = EIO;
return false;
}
}
return true;
}
void close_global_file() {
if(global_file) {
fclose(global_file);
global_file = nullptr;
}
}
bool extract_entry(frg::string_view line, passwd *entry) {
frg::string_view segments[8];
// Parse the line into 7 or 8 segments.
size_t s = 0;
int n;
for(n = 0; n < 7; n++) {
size_t d = line.find_first(':', s);
if(d == size_t(-1))
break;
segments[n] = line.sub_string(s, d - s);
s = d + 1;
}
if(line.find_first(':', s) != size_t(-1))
return false;
segments[n] = line.sub_string(s, line.size() - s);
n++;
if(n < 7)
return false;
// TODO: Handle strndup() failure.
auto name = strndup(segments[0].data(), segments[0].size());
__ensure(name);
auto passwd = strndup(segments[1].data(), segments[1].size());
__ensure(passwd);
auto uid = segments[2].to_number<int>();
if(!uid)
return false;
auto gid = segments[3].to_number<int>();
if(!gid)
return false;
auto real_name = strndup(segments[4].data(), segments[4].size());
__ensure(real_name);
auto dir = strndup(segments[5].data(), segments[5].size());
__ensure(dir);
auto shell = strndup(segments[6].data(), segments[6].size());
__ensure(shell);
// Chop the newline off the end of shell
__ensure(strlen(shell) > 0);
shell[strlen(shell) - 1] = '\0';
entry->pw_name = name;
entry->pw_passwd = passwd;
entry->pw_uid = *uid;
entry->pw_gid = *gid;
entry->pw_dir = dir;
entry->pw_shell = shell;
entry->pw_gecos = real_name;
return true;
}
void copy_to_buffer(passwd *pwd, char *buffer, size_t size) {
char *pw_dir = stpcpy(buffer, pwd->pw_name) + 1;
free(pwd->pw_name);
pwd->pw_name = buffer;
char *pw_shell = stpcpy(pw_dir, pwd->pw_dir) + 1;
free(pwd->pw_dir);
pwd->pw_dir = pw_dir;
char *pw_passwd = stpcpy(pw_shell, pwd->pw_shell) + 1;
free(pwd->pw_shell);
pwd->pw_shell = pw_shell;
char *end = stpcpy(pw_passwd, pwd->pw_passwd);
__ensure(end <= buffer + size);
free(pwd->pw_passwd);
pwd->pw_passwd = pw_passwd;
}
void clear_entry(passwd *entry) {
free(entry->pw_name);
free(entry->pw_dir);
free(entry->pw_passwd);
free(entry->pw_shell);
entry->pw_name = nullptr;
entry->pw_dir = nullptr;
entry->pw_passwd = nullptr;
entry->pw_shell = nullptr;
}
} // namespace
struct passwd *getpwent(void) {
static passwd entry;
char line[NSS_BUFLEN_PASSWD];
if(!open_global_file()) {
return nullptr;
}
if (fgets(line, NSS_BUFLEN_PASSWD, global_file)) {
clear_entry(&entry);
if(!extract_entry(line, &entry)) {
errno = EINVAL; // I suppose this can be a valid errno?
return nullptr;
}
return &entry;
}
if(ferror(global_file)) {
errno = EIO;
}
return nullptr;
}
struct passwd *getpwnam(const char *name) {
static passwd entry;
auto file = fopen("/etc/passwd", "r");
if(!file)
return nullptr;
char line[NSS_BUFLEN_PASSWD];
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
clear_entry(&entry);
if(!extract_entry(line, &entry))
continue;
if(!strcmp(entry.pw_name, name)) {
fclose(file);
return &entry;
}
}
int err = errno;
if(ferror(file)) {
err = EIO;
}
fclose(file);
errno = err;
return nullptr;
}
int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
*result = nullptr;
auto file = fopen("/etc/passwd", "r");
if(!file) {
return EIO;
}
char line[NSS_BUFLEN_PASSWD];
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
if(!extract_entry(line, pwd))
continue;
if(!strcmp(pwd->pw_name, name)) {
fclose(file);
size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
+ strlen(pwd->pw_shell) + strlen(pwd->pw_passwd) + 4;
if (size < required_size)
return ERANGE;
copy_to_buffer(pwd, buffer, size);
*result = pwd;
return 0;
}
}
int ret = 0;
if(ferror(file)) {
ret = EIO;
}
fclose(file);
return ret;
}
struct passwd *getpwuid(uid_t uid) {
static passwd entry;
auto file = fopen("/etc/passwd", "r");
if(!file)
return nullptr;
char line[NSS_BUFLEN_PASSWD];
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
clear_entry(&entry);
if(!extract_entry(line, &entry))
continue;
if(entry.pw_uid == uid) {
fclose(file);
return &entry;
}
}
int err = ESRCH;
if(ferror(file)) {
err = EIO;
}
fclose(file);
errno = err;
return nullptr;
}
int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) {
*result = nullptr;
auto file = fopen("/etc/passwd", "r");
if(!file) {
return EIO;
}
char line[NSS_BUFLEN_PASSWD];
while(fgets(line, NSS_BUFLEN_PASSWD, file)) {
if(!extract_entry(line, pwd))
continue;
if(pwd->pw_uid == uid) {
fclose(file);
size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir)
+ strlen(pwd->pw_shell) + + strlen(pwd->pw_passwd) + 4;
if (size < required_size)
return ERANGE;
copy_to_buffer(pwd, buffer, size);
*result = pwd;
return 0;
}
}
int ret = 0;
if(ferror(file)) {
ret = EIO;
}
fclose(file);
return ret;
}
void setpwent(void) {
if(!open_global_file()) {
return;
}
rewind(global_file);
}
void endpwent(void) {
close_global_file();
}
int putpwent(const struct passwd *p, FILE *f) {
auto invalid = [](const char *s) {
return s == nullptr || strchr(s, '\n') || strchr(s, ':');
};
if (p == nullptr || invalid(p->pw_name) || invalid(p->pw_passwd) || invalid(p->pw_gecos) || invalid(p->pw_dir) || invalid(p->pw_shell)) {
errno = EINVAL;
return -1;
}
// Taken from musl.
return fprintf(f, "%s:%s:%u:%u:%s:%s:%s\n", p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell) < 0 ? -1 : 0;
}
struct passwd *fgetpwent(FILE *file) {
static passwd entry;
char line[NSS_BUFLEN_PASSWD];
if (fgets(line, NSS_BUFLEN_PASSWD, file)) {
clear_entry(&entry);
if(!extract_entry(line, &entry)) {
errno = EINVAL; // I suppose this can be a valid errno?
return nullptr;
}
return &entry;
}
if(ferror(file)) {
errno = EIO;
}
return nullptr;
}