feature: login program

This commit is contained in:
Mark Poliakov 2021-11-28 11:46:55 +02:00
parent ed51f233ee
commit a7a0c8bf2c
17 changed files with 522 additions and 23 deletions

1
Cargo.lock generated
View File

@ -263,6 +263,7 @@ name = "user"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"libsys",
"libusr", "libusr",
] ]

View File

@ -92,11 +92,13 @@ initrd:
--target=../etc/$(ARCH)-osdev5.json \ --target=../etc/$(ARCH)-osdev5.json \
-Z build-std=core,alloc,compiler_builtins \ -Z build-std=core,alloc,compiler_builtins \
$(CARGO_COMMON_OPTS) $(CARGO_COMMON_OPTS)
mkdir -p $(O)/rootfs/bin mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev
touch $(O)/rootfs/dev/.do_no_remove
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin
cp target/$(ARCH)-osdev5/$(PROFILE)/login $(O)/rootfs/sbin
cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"` cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"`
ifeq ($(MACH),orangepi3) ifeq ($(MACH),orangepi3)
$(MKIMAGE) \ $(MKIMAGE) \

View File

@ -1,8 +1,8 @@
use crate::{FileRef, VnodeKind, VnodeRef}; use crate::{FileRef, VnodeKind, VnodeRef};
use libsys::{ use libsys::{
error::Errno, error::Errno,
stat::{OpenFlags, FileMode},
path::{path_component_left, path_component_right}, path::{path_component_left, path_component_right},
stat::{FileMode, GroupId, OpenFlags, UserId},
}; };
/// I/O context structure /// I/O context structure
@ -10,13 +10,17 @@ use libsys::{
pub struct Ioctx { pub struct Ioctx {
root: VnodeRef, root: VnodeRef,
cwd: VnodeRef, cwd: VnodeRef,
pub uid: UserId,
pub gid: GroupId,
} }
impl Ioctx { impl Ioctx {
/// Creates a new I/O context with given root node /// Creates a new I/O context with given root node
pub fn new(root: VnodeRef) -> Self { pub fn new(root: VnodeRef, uid: UserId, gid: GroupId) -> Self {
Self { Self {
cwd: root.clone(), cwd: root.clone(),
uid,
gid,
root, root,
} }
} }
@ -41,6 +45,11 @@ impl Ioctx {
} }
} }
while let Some(target) = at.target() {
assert!(at.kind() == VnodeKind::Directory);
at = target;
}
if element.is_empty() && rest.is_empty() { if element.is_empty() && rest.is_empty() {
return Ok(at); return Ok(at);
} }
@ -113,6 +122,15 @@ impl Ioctx {
node.open(opts) node.open(opts)
} }
pub fn chdir(&mut self, path: &str) -> Result<(), Errno> {
let node = self.find(None, path, true)?;
if !node.is_directory() {
return Err(Errno::NotADirectory);
}
self.cwd = node;
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -105,6 +105,7 @@ impl Vnode {
pub const SEEKABLE: u32 = 1 << 0; pub const SEEKABLE: u32 = 1 << 0;
pub const CACHE_READDIR: u32 = 1 << 1; pub const CACHE_READDIR: u32 = 1 << 1;
pub const CACHE_STAT: u32 = 1 << 2;
/// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node /// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node
/// then needs to have [Vnode::set_data()] called on it to be usable. /// then needs to have [Vnode::set_data()] called on it to be usable.
@ -452,7 +453,14 @@ impl Vnode {
/// Reports file status /// Reports file status
pub fn stat(self: &VnodeRef) -> Result<Stat, Errno> { pub fn stat(self: &VnodeRef) -> Result<Stat, Errno> {
if let Some(ref mut data) = *self.data() { if self.flags & Self::CACHE_STAT != 0 {
let props = self.props();
Ok(Stat {
blksize: 0,
size: 0,
mode: props.mode
})
} else if let Some(ref mut data) = *self.data() {
data.stat(self.clone()) data.stat(self.clone())
} else { } else {
Err(Errno::NotImplemented) Err(Errno::NotImplemented)

View File

@ -2,7 +2,7 @@
use crate::util::InitOnce; use crate::util::InitOnce;
use alloc::boxed::Box; use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, Ordering}; use core::sync::atomic::{AtomicUsize, Ordering};
use libsys::error::Errno; use libsys::{stat::FileMode, error::Errno};
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef}; use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef};
/// Possible character device kinds /// Possible character device kinds
@ -16,7 +16,9 @@ static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
/// Initializes devfs /// Initializes devfs
pub fn init() { pub fn init() {
DEVFS_ROOT.init(Vnode::new("", VnodeKind::Directory, 0)); let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
node.props_mut().mode = FileMode::default_dir();
DEVFS_ROOT.init(node);
} }
/// Returns devfs root node reference /// Returns devfs root node reference
@ -27,7 +29,8 @@ pub fn root() -> &'static VnodeRef {
fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> { fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
infoln!("Add char device: {}", name); infoln!("Add char device: {}", name);
let node = Vnode::new(name, VnodeKind::Char, 0); let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR;
node.set_data(Box::new(CharDeviceWrapper::new(dev))); node.set_data(Box::new(CharDeviceWrapper::new(dev)));
DEVFS_ROOT.get().attach(node); DEVFS_ROOT.get().attach(node);

View File

@ -3,6 +3,8 @@ use crate::mem::{
self, self,
phys::{self, PageUsage}, phys::{self, PageUsage},
}; };
use libsys::{error::Errno, stat::MountOptions};
use vfs::VnodeRef;
use memfs::BlockAllocator; use memfs::BlockAllocator;
pub mod devfs; pub mod devfs;
@ -25,3 +27,13 @@ unsafe impl BlockAllocator for MemfsBlockAlloc {
phys::free_page(phys).unwrap(); phys::free_page(phys).unwrap();
} }
} }
pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Errno> {
let fs_name = options.fs.unwrap();
if fs_name == "devfs" {
Ok(devfs::root().clone())
} else {
todo!();
}
}

View File

@ -4,7 +4,7 @@ use crate::config::{ConfigKey, CONFIG};
use crate::fs::{devfs, MemfsBlockAlloc}; use crate::fs::{devfs, MemfsBlockAlloc};
use crate::mem; use crate::mem;
use crate::proc::{elf, Process}; use crate::proc::{elf, Process};
use libsys::stat::{FileDescriptor, OpenFlags}; use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId};
use memfs::Ramfs; use memfs::Ramfs;
use vfs::{Filesystem, Ioctx}; use vfs::{Filesystem, Ioctx};
@ -29,7 +29,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() }; unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() };
let root = fs.root().unwrap(); let root = fs.root().unwrap();
let ioctx = Ioctx::new(root); let ioctx = Ioctx::new(root, UserId::root(), GroupId::root());
let node = ioctx.find(None, "/init", true).unwrap(); let node = ioctx.find(None, "/init", true).unwrap();
let file = node.open(OpenFlags::O_RDONLY | OpenFlags::O_EXEC).unwrap(); let file = node.open(OpenFlags::O_RDONLY | OpenFlags::O_EXEC).unwrap();

View File

@ -1,6 +1,6 @@
//! Process file descriptors and I/O context //! Process file descriptors and I/O context
use alloc::collections::BTreeMap; use alloc::collections::BTreeMap;
use libsys::{error::Errno, stat::FileDescriptor}; use libsys::{error::Errno, stat::{FileDescriptor, UserId, GroupId}};
use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind}; use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind};
/// Process I/O context. Contains file tables, root/cwd info etc. /// Process I/O context. Contains file tables, root/cwd info etc.
@ -31,6 +31,57 @@ impl ProcessIo {
self.ctty.clone() self.ctty.clone()
} }
#[inline(always)]
pub fn uid(&self) -> UserId {
self.ioctx.as_ref().unwrap().uid
}
#[inline(always)]
pub fn gid(&self) -> GroupId {
self.ioctx.as_ref().unwrap().gid
}
#[inline(always)]
pub fn set_uid(&mut self, uid: UserId) -> Result<(), Errno> {
let old_uid = self.uid();
if old_uid == uid {
Ok(())
} else if !old_uid.is_root() {
Err(Errno::PermissionDenied)
} else {
self.ioctx.as_mut().unwrap().uid = uid;
Ok(())
}
}
#[inline(always)]
pub fn set_gid(&mut self, gid: GroupId) -> Result<(), Errno> {
let old_gid = self.gid();
if old_gid == gid {
Ok(())
} else if !old_gid.is_root() {
Err(Errno::PermissionDenied)
} else {
self.ioctx.as_mut().unwrap().gid = gid;
Ok(())
}
}
pub fn duplicate_file(&mut self, src: FileDescriptor, dst: Option<FileDescriptor>) -> Result<FileDescriptor, Errno> {
let file_ref = self.file(src)?;
if let Some(dst) = dst {
let idx = u32::from(dst);
if self.files.get(&idx).is_some() {
return Err(Errno::AlreadyExists);
}
self.files.insert(idx, file_ref);
Ok(dst)
} else {
self.place_file(file_ref)
}
}
/// Returns [File] struct referred to by file descriptor `idx` /// Returns [File] struct referred to by file descriptor `idx`
pub fn file(&mut self, fd: FileDescriptor) -> Result<FileRef, Errno> { pub fn file(&mut self, fd: FileDescriptor) -> Result<FileRef, Errno> {
self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile) self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile)

View File

@ -82,6 +82,10 @@ impl Process {
self.inner.lock().pgid = pgid; self.inner.lock().pgid = pgid;
} }
pub fn set_sid(&self, sid: Pid) {
self.inner.lock().sid = sid;
}
#[inline] #[inline]
pub fn current() -> ProcessRef { pub fn current() -> ProcessRef {
Thread::current().owner().unwrap() Thread::current().owner().unwrap()

View File

@ -3,6 +3,7 @@
use crate::arch::{machine, platform::exception::ExceptionFrame}; use crate::arch::{machine, platform::exception::ExceptionFrame};
use crate::debug::Level; use crate::debug::Level;
use crate::dev::timer::TimestampSource; use crate::dev::timer::TimestampSource;
use crate::fs::create_filesystem;
use crate::proc::{self, elf, wait, Process, ProcessIo, Thread}; use crate::proc::{self, elf, wait, Process, ProcessIo, Thread};
use core::mem::size_of; use core::mem::size_of;
use core::ops::DerefMut; use core::ops::DerefMut;
@ -15,7 +16,8 @@ use libsys::{
proc::{ExitCode, Pid}, proc::{ExitCode, Pid},
signal::{Signal, SignalDestination}, signal::{Signal, SignalDestination},
stat::{ stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH, AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
OpenFlags, Stat, UserId, AT_EMPTY_PATH,
}, },
traits::{Read, Write}, traits::{Read, Write},
}; };
@ -107,7 +109,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let proc = Process::current(); let proc = Process::current();
let mut io = proc.io.lock(); let mut io = proc.io.lock();
let stat = find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?; let stat =
find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?;
*buf = stat; *buf = stat;
Ok(0) Ok(0)
} }
@ -153,6 +156,48 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
io.file(fd)?.borrow_mut().readdir(buf) io.file(fd)?.borrow_mut().readdir(buf)
} }
SystemCall::GetUserId => {
let proc = Process::current();
let uid = proc.io.lock().uid();
Ok(u32::from(uid) as usize)
}
SystemCall::GetGroupId => {
let proc = Process::current();
let gid = proc.io.lock().gid();
Ok(u32::from(gid) as usize)
}
SystemCall::DuplicateFd => {
let src = FileDescriptor::from(args[0] as u32);
let dst = FileDescriptor::from_i32(args[1] as i32)?;
let proc = Process::current();
let mut io = proc.io.lock();
let res = io.duplicate_file(src, dst)?;
Ok(u32::from(res) as usize)
}
SystemCall::SetUserId => {
let uid = UserId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_uid(uid)?;
Ok(0)
}
SystemCall::SetGroupId => {
let gid = GroupId::from(args[0] as u32);
let proc = Process::current();
proc.io.lock().set_gid(gid)?;
Ok(0)
}
SystemCall::SetCurrentDirectory => {
let path = arg::str_ref(args[0], args[1])?;
let proc = Process::current();
proc.io.lock().ioctx().chdir(path)?;
Ok(0)
}
SystemCall::GetCurrentDirectory => {
todo!()
}
// Process // Process
SystemCall::Clone => { SystemCall::Clone => {
@ -294,7 +339,9 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
todo!(); todo!();
} }
todo!(); let id = proc.id();
proc.set_sid(id);
Ok(id.value() as usize)
} }
SystemCall::SetPgid => { SystemCall::SetPgid => {
let pid = args[0] as u32; let pid = args[0] as u32;
@ -317,10 +364,28 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
let time = machine::local_timer().timestamp()?; let time = machine::local_timer().timestamp()?;
Ok(time.as_nanos() as usize) Ok(time.as_nanos() as usize)
} }
SystemCall::Mount => {
let target = arg::str_ref(args[0], args[1])?;
let options = arg::struct_ref::<MountOptions>(args[2])?;
let proc = Process::current();
let mut io = proc.io.lock();
debugln!("mount(target={:?}, options={:#x?})", target, options);
let target_node = io.ioctx().find(None, target, true)?;
let root = create_filesystem(options)?;
target_node.mount(root)?;
Ok(0)
}
// Debugging // Debugging
SystemCall::DebugTrace => { SystemCall::DebugTrace => {
let level = TraceLevel::from_repr(args[0]).map(Level::from).ok_or(Errno::InvalidArgument)?; let level = TraceLevel::from_repr(args[0])
.map(Level::from)
.ok_or(Errno::InvalidArgument)?;
let buf = arg::str_ref(args[1], args[2])?; let buf = arg::str_ref(args[1], args[2])?;
let thread = Thread::current(); let thread = Thread::current();
let proc = thread.owner().unwrap(); let proc = thread.owner().unwrap();

View File

@ -13,6 +13,13 @@ pub enum SystemCall {
Select = 7, Select = 7,
Access = 8, Access = 8,
ReadDirectory = 9, ReadDirectory = 9,
GetUserId = 10,
GetGroupId = 11,
DuplicateFd = 12,
SetUserId = 13,
SetGroupId = 14,
SetCurrentDirectory = 15,
GetCurrentDirectory = 16,
// Process manipulation // Process manipulation
Fork = 32, Fork = 32,
Clone = 33, Clone = 33,
@ -34,6 +41,7 @@ pub enum SystemCall {
SetPgid = 49, SetPgid = 49,
// System // System
GetCpuTime = 64, GetCpuTime = 64,
Mount = 65,
// Debugging // Debugging
DebugTrace = 128 DebugTrace = 128
} }

View File

@ -1,11 +1,14 @@
use crate::abi::SystemCall; use crate::abi::SystemCall;
use crate::{ use crate::{
error::Errno,
debug::TraceLevel, debug::TraceLevel,
error::Errno,
ioctl::IoctlCmd, ioctl::IoctlCmd,
proc::{ExitCode, Pid}, proc::{ExitCode, Pid},
signal::{Signal, SignalDestination}, signal::{Signal, SignalDestination},
stat::{AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat}, stat::{
AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions,
OpenFlags, Stat, UserId,
},
}; };
use core::time::Duration; use core::time::Duration;
@ -387,3 +390,64 @@ pub fn sys_readdir(fd: FileDescriptor, buf: &mut [DirectoryEntry]) -> Result<usi
) )
}) })
} }
#[inline(always)]
pub fn sys_getuid() -> UserId {
UserId::from(unsafe { syscall!(SystemCall::GetUserId) as u32 })
}
#[inline(always)]
pub fn sys_getgid() -> GroupId {
GroupId::from(unsafe { syscall!(SystemCall::GetGroupId) as u32 })
}
#[inline(always)]
pub fn sys_setsid() -> Result<Pid, Errno> {
Errno::from_syscall(unsafe { syscall!(SystemCall::SetSid) })
.map(|e| unsafe { Pid::from_raw(e as u32) })
}
#[inline(always)]
pub fn sys_mount(target: &str, options: &MountOptions) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::Mount,
argp!(target.as_ptr()),
argn!(target.len()),
argp!(options as *const _)
)
})
}
#[inline(always)]
pub fn sys_dup(src: FileDescriptor, dst: Option<FileDescriptor>) -> Result<FileDescriptor, Errno> {
Errno::from_syscall(unsafe {
syscall!(
SystemCall::DuplicateFd,
argn!(u32::from(src)),
argn!(FileDescriptor::into_i32(dst))
)
})
.map(|e| FileDescriptor::from(e as u32))
}
#[inline(always)]
pub fn sys_setuid(uid: UserId) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetUserId, u32::from(uid) as usize) })
}
#[inline(always)]
pub fn sys_setgid(gid: GroupId) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetGroupId, u32::from(gid) as usize) })
}
#[inline(always)]
pub fn sys_chdir(path: &str) -> Result<(), Errno> {
Errno::from_syscall_unit(unsafe {
syscall!(
SystemCall::SetCurrentDirectory,
argp!(path.as_ptr()),
argn!(path.len())
)
})
}

View File

@ -1,3 +1,4 @@
// TODO split up this file
use crate::error::Errno; use crate::error::Errno;
use core::fmt; use core::fmt;
@ -15,6 +16,7 @@ bitflags! {
const O_EXEC = 1 << 5; const O_EXEC = 1 << 5;
const O_CLOEXEC = 1 << 6; const O_CLOEXEC = 1 << 6;
const O_DIRECTORY = 1 << 7; const O_DIRECTORY = 1 << 7;
const O_CTTY = 1 << 8;
} }
} }
@ -23,6 +25,7 @@ bitflags! {
const FILE_TYPE = 0xF << 12; const FILE_TYPE = 0xF << 12;
const S_IFREG = 0x8 << 12; const S_IFREG = 0x8 << 12;
const S_IFDIR = 0x4 << 12; const S_IFDIR = 0x4 << 12;
const S_IFCHR = 0x2 << 12;
const USER_READ = 1 << 8; const USER_READ = 1 << 8;
const USER_WRITE = 1 << 7; const USER_WRITE = 1 << 7;
@ -45,6 +48,69 @@ bitflags! {
} }
} }
#[derive(Clone, Debug)]
pub struct MountOptions<'a> {
pub device: Option<&'a str>,
pub fs: Option<&'a str>,
// TODO flags etc.
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UserId(u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct GroupId(u32);
impl UserId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(self) -> bool {
self.0 == 0
}
}
impl From<u32> for UserId {
#[inline(always)]
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<UserId> for u32 {
#[inline(always)]
fn from(v: UserId) -> u32 {
v.0
}
}
impl GroupId {
pub const fn root() -> Self {
Self(0)
}
pub const fn is_root(self) -> bool {
self.0 == 0
}
}
impl From<u32> for GroupId {
#[inline(always)]
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<GroupId> for u32 {
#[inline(always)]
fn from(v: GroupId) -> u32 {
v.0
}
}
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct FdSet { pub struct FdSet {
bits: [u64; 2], bits: [u64; 2],
@ -191,6 +257,7 @@ impl fmt::Display for FileMode {
"{}{}{}{}{}{}{}{}{}{}", "{}{}{}{}{}{}{}{}{}{}",
// File type // File type
match *self & Self::FILE_TYPE { match *self & Self::FILE_TYPE {
Self::S_IFCHR => 'c',
Self::S_IFDIR => 'd', Self::S_IFDIR => 'd',
Self::S_IFREG => '-', Self::S_IFREG => '-',
_ => '?' _ => '?'

View File

@ -21,6 +21,11 @@ path = "src/fuzzy/main.rs"
name = "ls" name = "ls"
path = "src/ls/main.rs" path = "src/ls/main.rs"
[[bin]]
name = "login"
path = "src/login/main.rs"
[dependencies] [dependencies]
libusr = { path = "../libusr" } libusr = { path = "../libusr" }
libsys = { path = "../libsys" }
lazy_static = { version = "*", features = ["spin_no_std"] } lazy_static = { version = "*", features = ["spin_no_std"] }

View File

@ -5,8 +5,19 @@
#[macro_use] #[macro_use]
extern crate libusr; extern crate libusr;
use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitpid};
#[no_mangle] #[no_mangle]
fn main() -> i32 { fn main() -> i32 {
sys_mount(
"/dev",
&MountOptions {
device: None,
fs: Some("devfs"),
},
)
.expect("Failed to mount devfs");
let pid = unsafe { libusr::sys::sys_fork().unwrap() }; let pid = unsafe { libusr::sys::sys_fork().unwrap() };
if let Some(pid) = pid { if let Some(pid) = pid {
@ -20,7 +31,7 @@ fn main() -> i32 {
} }
} }
} else { } else {
libusr::sys::sys_execve("/bin/shell", &["/bin/shell"]).unwrap(); libusr::sys::sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap();
loop {} loop {}
} }
} }

151
user/src/login/main.rs Normal file
View File

@ -0,0 +1,151 @@
#![no_std]
#![no_main]
#[macro_use]
extern crate libusr;
#[macro_use]
extern crate alloc;
use libsys::{
calls::{
sys_close, sys_dup, sys_fork, sys_getgid, sys_getpgid, sys_getuid, sys_ioctl, sys_openat,
sys_read, sys_setgid, sys_setpgid, sys_setsid, sys_setuid, sys_waitpid, sys_execve
},
error::Errno,
ioctl::IoctlCmd,
proc::Pid,
stat::{FileDescriptor, FileMode, GroupId, OpenFlags, UserId},
termios::{Termios, TermiosLflag},
};
use libusr::{env, io};
struct HiddenInput {
fd: FileDescriptor,
termios: Termios,
}
impl HiddenInput {
fn open(fd: FileDescriptor) -> Result<Self, Errno> {
use core::mem::{size_of, MaybeUninit};
let mut termios: MaybeUninit<Termios> = MaybeUninit::uninit();
sys_ioctl(
fd,
IoctlCmd::TtyGetAttributes,
termios.as_mut_ptr() as usize,
size_of::<Termios>(),
)?;
let termios = unsafe { termios.assume_init() };
let mut new_termios = termios.clone();
new_termios.lflag &= !(TermiosLflag::ECHO | TermiosLflag::ECHOK | TermiosLflag::ECHOE);
sys_ioctl(
fd,
IoctlCmd::TtySetAttributes,
&new_termios as *const _ as usize,
size_of::<Termios>(),
)?;
Ok(Self { fd, termios })
}
fn readline<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a str, Errno> {
readline(self.fd, buf)
}
}
impl Drop for HiddenInput {
fn drop(&mut self) {
use core::mem::size_of;
sys_ioctl(
self.fd,
IoctlCmd::TtySetAttributes,
&self.termios as *const _ as usize,
size_of::<Termios>(),
)
.ok();
}
}
fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, Errno> {
let len = sys_read(fd, buf)?;
if len == 0 {
Ok("")
} else {
Ok(core::str::from_utf8(&buf[..len - 1]).unwrap())
}
}
fn login_as(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> {
if let Some(pid) = unsafe { sys_fork() }? {
let mut status = 0;
sys_waitpid(pid, &mut status).ok();
let pgid = sys_getpgid(unsafe { Pid::from_raw(0) }).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
Ok(())
} else {
sys_setuid(uid).expect("setuid failed");
sys_setgid(gid).expect("setgid failed");
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
sys_execve(shell, &[shell]).expect("execve() failed");
panic!();
}
}
// TODO baud rate and misc port settings
#[no_mangle]
fn main() -> i32 {
if !sys_getuid().is_root() {
panic!("This program must be run as root");
}
let args = env::args();
if args.len() != 2 {
panic!("Usage: {} TTY", args[0]);
}
sys_setsid().expect("setsid() failed");
// Close controlling terminal
// NOTE this will invalidate rust-side Stdin, Stdout, Stderr
// until replacement is re-opened using the specified TTY
sys_close(FileDescriptor::STDERR).unwrap();
sys_close(FileDescriptor::STDOUT).unwrap();
sys_close(FileDescriptor::STDIN).unwrap();
sys_openat(
None,
args[1],
FileMode::default_reg(),
OpenFlags::O_RDONLY | OpenFlags::O_CTTY,
)
.expect("Failed to open stdin");
sys_openat(
None,
args[1],
FileMode::default_reg(),
OpenFlags::O_WRONLY | OpenFlags::O_CTTY,
)
.expect("Failed to open stdout");
sys_dup(FileDescriptor::STDOUT, Some(FileDescriptor::STDERR)).expect("Failed to open stderr");
let mut user_buf = [0; 128];
let mut password_buf = [0; 128];
loop {
print!("login: ");
let username = readline(FileDescriptor::STDIN, &mut user_buf).expect("Login read failed");
print!("password: ");
let password = {
let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap();
input.readline(&mut password_buf)
}
.expect("Password read failed");
if username == "root" && password == "toor" {
login_as(UserId::from(0), GroupId::from(0), "/bin/shell");
}
}
0
}

View File

@ -9,11 +9,34 @@ use alloc::{borrow::ToOwned, vec::Vec};
use libusr::io::{self, Read}; use libusr::io::{self, Read};
use libusr::signal::{self, SignalHandler}; use libusr::signal::{self, SignalHandler};
use libusr::sys::{ use libusr::sys::{
proc::Pid, sys_execve, sys_setpgid, sys_exit, sys_fork, sys_getpgid, sys_waitpid, Errno, ExitCode, proc::Pid, sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid,
sys_faccessat, AccessMode, sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal,
FileDescriptor, Signal,
}; };
struct Builtin {
func: fn(&[&str]) -> ExitCode,
name: &'static str,
}
fn cmd_cd(args: &[&str]) -> ExitCode {
if args.len() != 2 {
eprintln!("Usage: cd DIR");
ExitCode::from(-1)
} else {
if let Err(err) = sys_chdir(args[1]) {
eprintln!("{}: {:?}", args[1], err);
ExitCode::from(-1)
} else {
ExitCode::from(0)
}
}
}
static BUILTINS: [Builtin; 1] = [Builtin {
name: "cd",
func: cmd_cd,
}];
fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a str>, io::Error> { fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a str>, io::Error> {
let size = f.read(bytes)?; let size = f.read(bytes)?;
Ok(if size == 0 { Ok(if size == 0 {
@ -32,6 +55,12 @@ fn execute(line: &str) -> Result<ExitCode, Errno> {
let args: Vec<&str> = line.split(' ').collect(); let args: Vec<&str> = line.split(' ').collect();
let cmd = args[0]; let cmd = args[0];
for item in BUILTINS.iter() {
if item.name == cmd {
return Ok((item.func)(&args));
}
}
let filename = "/bin/".to_owned() + cmd; let filename = "/bin/".to_owned() + cmd;
sys_faccessat(None, &filename, AccessMode::X_OK, 0)?; sys_faccessat(None, &filename, AccessMode::X_OK, 0)?;
@ -73,12 +102,12 @@ fn main() -> i32 {
if let Err(e) = execute(line) { if let Err(e) = execute(line) {
eprintln!("{}: {:?}", line.split(' ').next().unwrap(), e); eprintln!("{}: {:?}", line.split(' ').next().unwrap(), e);
} }
}, }
Err(_) => { Err(_) => {
println!("Interrupt!"); println!("Interrupt!");
continue; continue;
}, }
_ => panic!() _ => panic!(),
} }
} }
0 0