diff options
Diffstat (limited to 'lib/mlibc/options/posix/generic/pwd-stubs.cpp')
-rw-r--r-- | lib/mlibc/options/posix/generic/pwd-stubs.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/pwd-stubs.cpp b/lib/mlibc/options/posix/generic/pwd-stubs.cpp new file mode 100644 index 0000000..5d8f618 --- /dev/null +++ b/lib/mlibc/options/posix/generic/pwd-stubs.cpp @@ -0,0 +1,284 @@ + +#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; + } +} + +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 *, FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} + +struct passwd *fgetpwent(FILE *) { + __ensure(!"Not implemented"); + __builtin_unreachable(); +} |