user: implement mlibc as the libc, finally.
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user