Parcourir la source

Implement pwd.h

jD91mZM2 il y a 6 ans
Parent
commit
f6b364845e

+ 11 - 0
Cargo.lock

@@ -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 - 0
Cargo.toml

@@ -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
include/sys/types.h

@@ -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 - 0
src/lib.rs

@@ -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 - 0
src/platform/src/lib.rs

@@ -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
src/platform/src/linux/mod.rs

@@ -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
src/platform/src/rawfile.rs

@@ -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
src/platform/src/redox/mod.rs

@@ -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
src/platform/src/types.rs

@@ -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 - 0
src/pwd/Cargo.toml

@@ -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 - 0
src/pwd/build.rs

@@ -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 - 0
src/pwd/cbindgen.toml

@@ -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 - 0
src/pwd/src/lib.rs

@@ -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 - 0
tests/.gitignore

@@ -64,6 +64,7 @@ wchar/mbsrtowcs
 wchar/putwchar
 wchar/wcrtomb
 dirent
+pwd
 stdlib/alloc
 stdlib/bsearch
 stdlib/mktemp

+ 1 - 0
tests/Makefile

@@ -67,6 +67,7 @@ EXPECT_BINS=\
 BINS=\
 	$(EXPECT_BINS) \
 	dirent \
+	pwd \
 	stdlib/alloc \
 	stdlib/bsearch \
 	stdlib/mktemp \

+ 82 - 0
tests/pwd.c

@@ -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 👍");
+}