blob: 5a0c191e38260e37f9bde44a7f9c386c61085c14 [file]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Helper functions for pwd.
///
//===----------------------------------------------------------------------===//
#include "src/pwd/pwd_utils.h"
#include "hdr/errno_macros.h"
#include "hdr/stdio_macros.h"
#include "hdr/types/struct_passwd.h"
#include "src/__support/CPP/span.h"
#include "src/__support/File/file.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/str_to_integer.h"
#include "src/string/string_utils.h"
#ifndef LIBC_COPT_PWD_FILE_PATH
#define LIBC_COPT_PWD_FILE_PATH "/etc/passwd"
#endif
namespace LIBC_NAMESPACE_DECL {
namespace internal {
ErrorOr<struct passwd> parse_passwd_line(char *line) {
if (!line)
return Error(EINVAL);
struct passwd pwd;
char *context = line;
pwd.pw_name = string_token<false>(nullptr, ":", &context);
if (!pwd.pw_name)
return Error(EINVAL);
pwd.pw_passwd = string_token<false>(nullptr, ":", &context);
if (!pwd.pw_passwd)
return Error(EINVAL);
char *uid_str = string_token<false>(nullptr, ":", &context);
if (!uid_str || !isdigit(uid_str[0]))
return Error(EINVAL);
auto uid_res = strtointeger<uid_t>(uid_str, 10);
if (uid_res.has_error() || uid_res.parsed_len == 0 ||
uid_str[uid_res.parsed_len] != '\0')
return Error(EINVAL);
pwd.pw_uid = uid_res.value;
char *gid_str = string_token<false>(nullptr, ":", &context);
if (!gid_str || !isdigit(gid_str[0]))
return Error(EINVAL);
auto gid_res = strtointeger<gid_t>(gid_str, 10);
if (gid_res.has_error() || gid_res.parsed_len == 0 ||
gid_str[gid_res.parsed_len] != '\0')
return Error(EINVAL);
pwd.pw_gid = gid_res.value;
pwd.pw_gecos = string_token<false>(nullptr, ":", &context);
if (!pwd.pw_gecos)
return Error(EINVAL);
pwd.pw_dir = string_token<false>(nullptr, ":", &context);
if (!pwd.pw_dir)
return Error(EINVAL);
pwd.pw_shell = string_token<false>(nullptr, ":", &context);
if (!pwd.pw_shell)
return Error(EINVAL);
return pwd;
}
} // namespace internal
namespace passwd {
static File *pwd_file = nullptr;
static const char *pwd_file_path = LIBC_COPT_PWD_FILE_PATH;
// Note: These static buffers are process-global and NOT protected by a mutex
// at this stage. POSIX getpwent is non-reentrant.
static char line_buffer[1024];
static struct passwd pwd_entry;
void TESTONLY_set_passwd_path(const char *path) {
if (!path)
return;
if (pwd_file) {
pwd_file->close();
pwd_file = nullptr;
}
pwd_file_path = path;
}
ErrorOr<void> open() {
if (!pwd_file) {
auto result = openfile(pwd_file_path, "r");
if (!result.has_value())
return Error(result.error());
pwd_file = result.value();
} else {
auto result = pwd_file->seek(0, SEEK_SET);
if (!result.has_value())
return Error(result.error());
}
return {};
}
ErrorOr<void> close() {
if (pwd_file) {
int result = pwd_file->close();
pwd_file = nullptr;
if (result != 0)
return Error(result);
}
return {};
}
struct ReadLineResult {
size_t bytes_read;
bool truncated;
};
// Reads a line from the given file into buf.
static ErrorOr<ReadLineResult> read_line(File *f, cpp::span<char> buf) {
if (!f || buf.empty())
return Error(EINVAL);
f->lock();
size_t bytes_read = 0;
FileIOResult result(0);
bool truncated = false;
for (char &ch : buf.first(buf.size() - 1)) {
result = f->read_unlocked(&ch, 1);
if (result.has_error()) {
f->unlock();
return Error(result.error);
}
if (result.value != 1)
break;
++bytes_read;
if (ch == '\n')
break;
}
if (result.value == 1 && bytes_read > 0 && buf[bytes_read - 1] != '\n') {
truncated = true;
char c = '\0';
while (true) {
result = f->read_unlocked(&c, 1);
if (result.has_error()) {
f->unlock();
return Error(result.error);
}
if (result.value != 1 || c == '\n')
break;
}
}
bool has_error = f->error_unlocked();
f->unlock();
if (has_error)
return Error(EIO);
buf[bytes_read] = '\0';
return ReadLineResult{bytes_read, truncated};
}
ErrorOr<struct passwd *> read_next() {
if (!pwd_file) {
auto result = open();
if (!result.has_value())
return Error(result.error());
}
while (true) {
auto result = read_line(pwd_file, line_buffer);
if (!result.has_value())
return Error(result.error());
ReadLineResult res = result.value();
if (res.bytes_read == 0)
return nullptr;
if (res.truncated)
return Error(EINVAL);
size_t len = res.bytes_read;
if (len > 0 && line_buffer[len - 1] == '\n')
line_buffer[len - 1] = '\0';
auto passwd_or = internal::parse_passwd_line(line_buffer);
if (!passwd_or.has_value())
return Error(passwd_or.error());
pwd_entry = passwd_or.value();
return &pwd_entry;
}
}
} // namespace passwd
} // namespace LIBC_NAMESPACE_DECL