Implement pwd.h

This commit is contained in:
jD91mZM2
2018-07-27 15:13:00 +02:00
parent 1b40f2d463
commit f6b364845e
16 changed files with 439 additions and 48 deletions
Generated
+11
View File
@@ -249,6 +249,16 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pwd"
version = "0.1.0"
dependencies = [
"cbindgen 0.5.2",
"errno 0.1.0",
"fcntl 0.1.0",
"platform 0.1.0",
]
[[package]]
name = "quote"
version = "0.3.15"
@@ -324,6 +334,7 @@ dependencies = [
"locale 0.1.0",
"netinet 0.1.0",
"platform 0.1.0",
"pwd 0.1.0",
"semaphore 0.1.0",
"setjmp 0.1.0",
"signal 0.1.0",
+1
View File
@@ -26,6 +26,7 @@ inttypes = { path = "src/inttypes" }
locale = { path = "src/locale" }
netinet = { path = "src/netinet" }
platform = { path = "src/platform" }
pwd = { path = "src/pwd" }
semaphore = { path = "src/semaphore" }
setjmp = { path = "src/setjmp" }
signal = { path = "src/signal" }
+7 -17
View File
@@ -2,41 +2,31 @@
#define _SYS_TYPES_H
typedef long blksize_t;
typedef long dev_t;
typedef unsigned long ino_t;
typedef int gid_t;
typedef int uid_t;
typedef int mode_t;
typedef unsigned long nlink_t;
typedef long off_t;
typedef int pid_t;
typedef unsigned id_t;
typedef long ssize_t;
typedef long time_t;
typedef unsigned int useconds_t;
typedef int suseconds_t;
typedef long clock_t;
typedef int clockid_t;
typedef void* timer_t;
typedef unsigned long int blkcnt_t;
typedef unsigned char u_char, uchar;
typedef unsigned short u_short, ushort;
typedef unsigned int u_int, uint;
typedef unsigned long u_long, ulong;
typedef long long quad_t;
typedef unsigned long long u_quad_t;
#ifdef __linux__
#define _SC_PAGE_SIZE 30
#endif
+1
View File
@@ -16,6 +16,7 @@ pub extern crate float;
pub extern crate grp;
pub extern crate locale;
pub extern crate netinet;
pub extern crate pwd;
pub extern crate semaphore;
pub extern crate setjmp;
pub extern crate signal;
+5
View File
@@ -33,8 +33,11 @@ mod sys;
#[path = "redox/mod.rs"]
mod sys;
pub mod rawfile;
pub mod types;
pub use rawfile::RawFile;
use alloc::Vec;
use core::{fmt, ptr};
@@ -61,6 +64,7 @@ pub unsafe fn c_str_mut<'a>(s: *mut c_char) -> &'a mut [u8] {
}
pub unsafe fn c_str_n_mut<'a>(s: *mut c_char, n: usize) -> &'a mut [u8] {
assert!(s != ptr::null_mut());
use core::slice;
let mut size = 0;
@@ -81,6 +85,7 @@ pub unsafe fn c_str<'a>(s: *const c_char) -> &'a [u8] {
}
pub unsafe fn c_str_n<'a>(s: *const c_char, n: usize) -> &'a [u8] {
assert!(s != ptr::null());
use core::slice;
let mut size = 0;
+7 -7
View File
@@ -122,15 +122,15 @@ pub fn getdents(fd: c_int, dirents: *mut dirent, bytes: usize) -> c_int {
}
pub fn getegid() -> gid_t {
e(unsafe { syscall!(GETEGID) })
e(unsafe { syscall!(GETEGID) }) as gid_t
}
pub fn geteuid() -> uid_t {
e(unsafe { syscall!(GETEUID) })
e(unsafe { syscall!(GETEUID) }) as uid_t
}
pub fn getgid() -> gid_t {
e(unsafe { syscall!(GETGID) })
e(unsafe { syscall!(GETGID) }) as gid_t
}
pub unsafe fn gethostname(mut name: *mut c_char, len: size_t) -> c_int {
@@ -174,15 +174,15 @@ pub unsafe fn getpeername(
}
pub fn getpgid(pid: pid_t) -> pid_t {
e(unsafe { syscall!(GETPGID, pid) })
e(unsafe { syscall!(GETPGID, pid) }) as pid_t
}
pub fn getpid() -> pid_t {
e(unsafe { syscall!(GETPID) })
e(unsafe { syscall!(GETPID) }) as pid_t
}
pub fn getppid() -> pid_t {
e(unsafe { syscall!(GETPPID) })
e(unsafe { syscall!(GETPPID) }) as pid_t
}
pub unsafe fn getsockname(
@@ -217,7 +217,7 @@ pub fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int {
}
pub fn getuid() -> uid_t {
e(unsafe { syscall!(GETUID) })
e(unsafe { syscall!(GETUID) }) as uid_t
}
pub fn ioctl(fd: c_int, request: c_ulong, out: *mut c_void) -> c_int {
+3 -3
View File
@@ -11,7 +11,7 @@ impl RawFile {
}
}
pub fn dup(&self, _buf: &[u8]) -> Result<RawFile, ()> {
pub fn dup(&self) -> Result<RawFile, ()> {
match dup(self.0) {
-1 => Err(()),
n => Ok(RawFile(n))
@@ -38,9 +38,9 @@ impl Drop for RawFile {
}
impl Deref for RawFile {
type Target = usize;
type Target = c_int;
fn deref(&self) -> &usize {
fn deref(&self) -> &c_int {
&self.0
}
}
+6 -6
View File
@@ -100,7 +100,7 @@ pub fn chmod(path: *const c_char, mode: mode_t) -> c_int {
match syscall::open(path, O_WRONLY) {
Err(err) => e(Err(err)) as c_int,
Ok(fd) => {
let res = syscall::fchmod(fd as usize, mode);
let res = syscall::fchmod(fd as usize, mode as u16);
let _ = syscall::close(fd);
e(res) as c_int
}
@@ -215,7 +215,7 @@ pub fn fchdir(fd: c_int) -> c_int {
}
pub fn fchmod(fd: c_int, mode: mode_t) -> c_int {
e(syscall::fchmod(fd as usize, mode)) as c_int
e(syscall::fchmod(fd as usize, mode as u16)) as c_int
}
pub fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int {
@@ -239,7 +239,7 @@ pub fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
(*buf).st_dev = redox_buf.st_dev as dev_t;
(*buf).st_ino = redox_buf.st_ino as ino_t;
(*buf).st_nlink = redox_buf.st_nlink as nlink_t;
(*buf).st_mode = redox_buf.st_mode;
(*buf).st_mode = redox_buf.st_mode as mode_t;
(*buf).st_uid = redox_buf.st_uid as uid_t;
(*buf).st_gid = redox_buf.st_gid as gid_t;
// TODO st_rdev
@@ -494,11 +494,11 @@ pub fn isatty(fd: c_int) -> c_int {
}
pub fn kill(pid: pid_t, sig: c_int) -> c_int {
e(syscall::kill(pid, sig as usize)) as c_int
e(syscall::kill(pid as usize, sig as usize)) as c_int
}
pub fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
e(syscall::kill(-(pgrp as isize) as pid_t, sig as usize)) as c_int
e(syscall::kill(-(pgrp as isize) as usize, sig as usize)) as c_int
}
pub fn link(path1: *const c_char, path2: *const c_char) -> c_int {
@@ -823,7 +823,7 @@ pub fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {
if !stat_loc.is_null() {
*stat_loc = temp as c_int;
}
res
res as pid_t
}
}
+15 -15
View File
@@ -50,25 +50,25 @@ pub type wchar_t = i32;
pub type wint_t = u32;
pub type wctype_t = i64;
pub type off_t = i64;
pub type mode_t = u16;
pub type time_t = i64;
pub type pid_t = usize;
pub type id_t = usize;
pub type gid_t = usize;
pub type uid_t = usize;
pub type dev_t = usize;
pub type ino_t = usize;
pub type nlink_t = usize;
pub type blksize_t = isize;
pub type blkcnt_t = u64;
pub type off_t = c_long;
pub type mode_t = c_int;
pub type time_t = c_long;
pub type pid_t = c_int;
pub type id_t = c_uint;
pub type gid_t = c_int;
pub type uid_t = c_int;
pub type dev_t = c_long;
pub type ino_t = c_ulong;
pub type nlink_t = c_ulong;
pub type blksize_t = c_long;
pub type blkcnt_t = c_ulong;
pub type useconds_t = c_uint;
pub type suseconds_t = c_int;
pub type clock_t = i64;
pub type clockid_t = i32;
pub type timer_t = c_void;
pub type clock_t = c_long;
pub type clockid_t = c_int;
pub type timer_t = *mut c_void;
#[repr(C)]
#[derive(Default)]
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "pwd"
version = "0.1.0"
authors = ["jD91mZM2 <me@krake.one>"]
[build-dependencies]
cbindgen = { path = "../../cbindgen" }
[dependencies]
errno = { path = "../errno" }
fcntl = { path = "../fcntl" }
platform = { path = "../platform" }
+11
View File
@@ -0,0 +1,11 @@
extern crate cbindgen;
use std::{env, fs};
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
fs::create_dir_all("../../target/include").expect("failed to create include directory");
cbindgen::generate(crate_dir)
.expect("failed to generate bindings")
.write_to_file("../../target/include/pwd.h");
}
+7
View File
@@ -0,0 +1,7 @@
sys_includes = ["stddef.h", "sys/types.h"]
include_guard = "_PWD_H"
language = "C"
style = "Tag"
[enum]
prefix_with_name = true
+269
View File
@@ -0,0 +1,269 @@
//! pwd implementation for relibc
#![no_std]
#![feature(alloc)]
extern crate alloc;
extern crate errno;
extern crate fcntl;
extern crate platform;
use alloc::vec::Vec;
use core::ptr;
use platform::RawFile;
use platform::types::*;
#[repr(C)]
pub struct passwd {
pw_name: *mut c_char,
pw_passwd: *mut c_char,
pw_uid: uid_t,
pw_gid: gid_t,
pw_gecos: *mut c_char,
pw_dir: *mut c_char,
pw_shell: *mut c_char
}
static mut PASSWD_BUF: *mut c_char = ptr::null_mut();
static mut PASSWD: passwd = passwd {
pw_name: ptr::null_mut(),
pw_passwd: ptr::null_mut(),
pw_uid: 0,
pw_gid: 0,
pw_gecos: ptr::null_mut(),
pw_dir: ptr::null_mut(),
pw_shell: ptr::null_mut()
};
enum OptionPasswd {
Error,
NotFound,
Found(*mut c_char)
}
fn pwd_lookup<F>(out: *mut passwd, alloc: Option<(*mut c_char, size_t)>, mut callback: F) -> OptionPasswd
where
// TODO F: FnMut(impl Iterator<Item = &[u8]>) -> bool
F: FnMut(&[&[u8]]) -> bool
{
let file = match RawFile::open("/etc/passwd\0".as_ptr() as *const c_char, fcntl::O_RDONLY, 0o644) {
Ok(file) => file,
Err(_) => return OptionPasswd::Error
};
let mut buf = Vec::new();
let mut newline = None;
loop {
// TODO when nll becomes a thing:
// let mut newline;
// WORKAROUND:
if let Some(newline) = newline {
buf.drain(..newline + 1);
}
// Read until newline
loop {
newline = buf.iter().position(|b| *b == b'\n');
if newline.is_some() {
break;
}
let len = buf.len();
if len >= buf.capacity() {
buf.reserve(1024);
}
unsafe {
let capacity = buf.capacity();
buf.set_len(capacity);
}
let read = platform::read(*file, &mut buf[len..]);
if read == 0 {
return OptionPasswd::NotFound;
}
if read < 0 {
return OptionPasswd::Error;
}
unsafe {
buf.set_len(len + read as usize);
}
}
// Parse into passwd
let newline = newline.unwrap(); // safe because it doesn't break the loop otherwise
let line = &buf[..newline];
let mut parts: [&[u8]; 7] = [&[]; 7];
for (i, part) in line.splitn(7, |b| *b == b':').enumerate() {
parts[i] = part;
}
if !callback(&parts) {
// TODO when nll becomes a thing:
// buf.drain(..newline + 1);
continue;
}
let len = parts.iter()
.enumerate()
.filter(|(i, _)| *i != 2 && *i != 3)
.map(|(_, part)| part.len() + 1)
.sum();
if alloc.map(|(_, s)| len > s as usize).unwrap_or(false) {
unsafe {
platform::errno = errno::ERANGE;
}
return OptionPasswd::Error;
}
let alloc = match alloc {
Some((alloc, _)) => alloc,
None => unsafe { platform::alloc(len) as *mut c_char }
};
// _ prefix so it won't complain about the trailing
// _off += <thing>
// in the macro that is never read
let mut _off = 0;
let mut parts = parts.into_iter();
macro_rules! copy_into {
($entry:expr) => {
debug_assert!(_off as usize <= len);
let src = parts.next().unwrap_or(&(&[] as &[u8])); // this is madness
let dst = unsafe { alloc.offset(_off) };
for (i, c) in src.iter().enumerate() {
unsafe {
*dst.offset(i as isize) = *c as c_char;
}
}
unsafe {
*dst.offset(src.len() as isize) = 0;
$entry = dst;
}
_off += src.len() as isize + 1;
};
($entry:expr, parse) => {
unsafe {
$entry = parts.next()
.and_then(|part| core::str::from_utf8(part).ok())
.and_then(|part| part.parse().ok())
.unwrap_or(0);
}
}
}
copy_into!((*out).pw_name);
copy_into!((*out).pw_passwd);
copy_into!((*out).pw_uid, parse);
copy_into!((*out).pw_gid, parse);
copy_into!((*out).pw_gecos);
copy_into!((*out).pw_dir);
copy_into!((*out).pw_shell);
return OptionPasswd::Found(alloc);
}
}
#[no_mangle]
pub extern "C" fn getpwnam_r(name: *const c_char, out: *mut passwd, buf: *mut c_char,
size: size_t, result: *mut *mut passwd) -> c_int {
match pwd_lookup(out, Some((buf, size)), |parts| {
let part = parts.get(0).unwrap_or(&(&[] as &[u8]));
for (i, c) in part.iter().enumerate() {
// /etc/passwd should not contain any NUL bytes in the middle
// of entries, but if this happens, it can't possibly match the
// search query since it's NUL terminated.
if *c == 0 || unsafe { *name.offset(i as isize) } != *c as c_char {
return false;
}
}
true
}) {
OptionPasswd::Error => unsafe {
*result = ptr::null_mut();
-1
},
OptionPasswd::NotFound => unsafe {
*result = ptr::null_mut();
0
},
OptionPasswd::Found(_) => unsafe {
*result = out;
0
}
}
}
#[no_mangle]
pub extern "C" fn getpwuid_r(uid: uid_t, out: *mut passwd, buf: *mut c_char,
size: size_t, result: *mut *mut passwd) -> c_int {
match pwd_lookup(out, Some((buf, size)), |parts| {
let part = parts.get(2)
.and_then(|part| core::str::from_utf8(part).ok())
.and_then(|part| part.parse().ok());
part == Some(uid)
}) {
OptionPasswd::Error => unsafe {
*result = ptr::null_mut();
-1
},
OptionPasswd::NotFound => unsafe {
*result = ptr::null_mut();
0
},
OptionPasswd::Found(_) => unsafe {
*result = out;
0
}
}
}
#[no_mangle]
pub extern "C" fn getpwnam(name: *const c_char) -> *mut passwd {
match pwd_lookup(unsafe { &mut PASSWD }, None, |parts| {
let part = parts.get(0).unwrap_or(&(&[] as &[u8]));
for (i, c) in part.iter().enumerate() {
// /etc/passwd should not contain any NUL bytes in the middle
// of entries, but if this happens, it can't possibly match the
// search query since it's NUL terminated.
if *c == 0 || unsafe { *name.offset(i as isize) } != *c as c_char {
return false;
}
}
true
}) {
OptionPasswd::Error => ptr::null_mut(),
OptionPasswd::NotFound => ptr::null_mut(),
OptionPasswd::Found(buf) => unsafe {
PASSWD_BUF = buf;
&mut PASSWD
}
}
}
#[no_mangle]
pub extern "C" fn getpwuid(uid: uid_t) -> *mut passwd {
match pwd_lookup(unsafe { &mut PASSWD }, None, |parts| {
let part = parts.get(2)
.and_then(|part| core::str::from_utf8(part).ok())
.and_then(|part| part.parse().ok());
part == Some(uid)
}) {
OptionPasswd::Error => ptr::null_mut(),
OptionPasswd::NotFound => ptr::null_mut(),
OptionPasswd::Found(buf) => unsafe {
PASSWD_BUF = buf;
&mut PASSWD
}
}
}
+1
View File
@@ -64,6 +64,7 @@ wchar/mbsrtowcs
wchar/putwchar
wchar/wcrtomb
dirent
pwd
stdlib/alloc
stdlib/bsearch
stdlib/mktemp
+1
View File
@@ -67,6 +67,7 @@ EXPECT_BINS=\
BINS=\
$(EXPECT_BINS) \
dirent \
pwd \
stdlib/alloc \
stdlib/bsearch \
stdlib/mktemp \
+82
View File
@@ -0,0 +1,82 @@
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
void print(struct passwd *pwd) {
printf("pw_name: %s\n", pwd->pw_name);
printf("pw_password: %s\n", pwd->pw_passwd);
printf("pw_uid: %u\n", pwd->pw_uid);
printf("pw_gid: %u\n", pwd->pw_gid);
printf("pw_gecos: %s\n", pwd->pw_gecos);
printf("pw_dir: %s\n", pwd->pw_dir);
printf("pw_shell: %s\n", pwd->pw_shell);
}
int main() {
puts("--- Checking getpwuid ---");
errno = 0;
struct passwd *pwd = getpwuid(0);
if (errno != 0) {
perror("getpwuid");
return 1;
}
if (pwd != NULL) {
print(pwd);
}
puts("--- Checking getpwnam ---");
errno = 0;
pwd = getpwnam("nobody");
if (errno != 0) {
perror("getpwnam");
return 1;
}
if (pwd != NULL) {
print(pwd);
}
puts("--- Checking getpwuid_r ---");
struct passwd pwd2;
struct passwd* result;
char* buf = malloc(100);
if (getpwuid_r(0, &pwd2, buf, 100, &result) < 0) {
perror("getpwuid_r");
free(buf);
return 1;
}
if (result != NULL) {
if (result != &pwd2) {
free(buf);
return 1;
}
print(&pwd2);
}
puts("--- Checking getpwnam_r ---");
if (getpwnam_r("nobody", &pwd2, buf, 100, &result) < 0) {
perror("getpwuid_r");
free(buf);
return 1;
}
if (result != NULL) {
if (result != &pwd2) {
free(buf);
return 1;
}
print(&pwd2);
}
free(buf);
puts("--- Checking getpwuid_r error handling ---");
char buf2[1];
if (getpwuid_r(0, &pwd2, buf2, 1, &result) == 0) {
puts("This shouldn't have succeeded, but did!");
return 1;
}
if (errno != ERANGE) {
perror("getpwuid_r");
return 1;
}
puts("Returned ERANGE because the buffer was too small 👍");
}